Skip to content

Commit 93ac5f6

Browse files
author
Bart Koelman
committed
Various corrections in documentation, added #nullable where it makes a difference
1 parent 760df70 commit 93ac5f6

File tree

11 files changed

+125
-72
lines changed

11 files changed

+125
-72
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,23 @@ See [our documentation](https://www.jsonapi.net/) for detailed usage.
4343
### Models
4444

4545
```c#
46-
public class Article : Identifiable
46+
#nullable enable
47+
48+
public class Article : Identifiable<int>
4749
{
4850
[Attr]
49-
public string Name { get; set; }
51+
public string Name { get; set; } = null!;
5052
}
5153
```
5254

5355
### Controllers
5456

5557
```c#
56-
public class ArticlesController : JsonApiController<Article>
58+
public class ArticlesController : JsonApiController<Article, int>
5759
{
58-
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
59-
IResourceService<Article> resourceService,)
60-
: base(options, loggerFactory, resourceService)
60+
public ArticlesController(IJsonApiOptions options, IResourceGraph resourceGraph,
61+
ILoggerFactory loggerFactory, IResourceService<Article, int> resourceService)
62+
: base(options, resourceGraph, loggerFactory, resourceService)
6163
{
6264
}
6365
}

docs/getting-started/step-by-step.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ Define your domain models such that they implement `IIdentifiable<TId>`.
3838
The easiest way to do this is to inherit from `Identifiable<TId>`.
3939

4040
```c#
41+
#nullable enable
42+
4143
public class Person : Identifiable<int>
4244
{
4345
[Attr]
44-
public string Name { get; set; }
46+
public string Name { get; set; } = null!;
4547
}
4648
```
4749

@@ -52,12 +54,12 @@ Nothing special here, just an ordinary `DbContext`.
5254
```
5355
public class AppDbContext : DbContext
5456
{
57+
public DbSet<Person> People => Set<Person>();
58+
5559
public AppDbContext(DbContextOptions<AppDbContext> options)
5660
: base(options)
5761
{
5862
}
59-
60-
public DbSet<Person> People { get; set; }
6163
}
6264
```
6365

docs/home/index.html

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,31 +142,35 @@ <h2>Example usage</h2>
142142
<div class="icon"><i class='bx bx-detail'></i></div>
143143
<h4 class="title">Resource</h4>
144144
<pre>
145-
<code>public class Article : Identifiable
145+
<code>#nullable enable
146+
147+
public class Article : Identifiable&lt;long&gt;
146148
{
147149
[Attr]
148-
[Required, MaxLength(30)]
149-
public string Title { get; set; }
150+
[MaxLength(30)]
151+
public string Title { get; set; } = null!;
150152

151153
[Attr(Capabilities = AttrCapabilities.AllowFilter)]
152-
public string Summary { get; set; }
154+
public string? Summary { get; set; }
153155

154156
[Attr(PublicName = "websiteUrl")]
155-
public string Url { get; set; }
157+
public string? Url { get; set; }
158+
159+
[Attr]
160+
[Required]
161+
public int? WordCount { get; set; }
156162

157163
[Attr(Capabilities = AttrCapabilities.AllowView)]
158164
public DateTimeOffset LastModifiedAt { get; set; }
159165

160166
[HasOne]
161-
public Person Author { get; set; }
167+
public Person Author { get; set; } = null!;
162168

163-
[HasMany]
164-
public ICollection&lt;Revision&gt; Revisions { get; set; }
169+
[HasOne]
170+
public Person? Reviewer { get; set; }
165171

166-
[HasManyThrough(nameof(ArticleTags))]
167-
[NotMapped]
168-
public ICollection&lt;Tag&gt; Tags { get; set; }
169-
public ICollection&lt;ArticleTag&gt; ArticleTags { get; set; }
172+
[HasMany]
173+
public ICollection&lt;Tag&gt; Tags { get; set; } = new HashSet&lt;Tag&gt;();
170174
}</code>
171175
</pre>
172176
</div>
@@ -179,7 +183,7 @@ <h4 class="title">Resource</h4>
179183
<h4 class="title">Request</h4>
180184
<pre>
181185
<code>
182-
GET /articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields=title,summary&include=author HTTP/1.1
186+
GET /articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields[articles]=title,summary&include=author HTTP/1.1
183187
</code>
184188
</pre>
185189
</div>
@@ -197,9 +201,9 @@ <h4 class="title">Response</h4>
197201
"totalResources": 1
198202
},
199203
"links": {
200-
"self": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields=title,summary&include=author",
201-
"first": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields=title,summary&include=author",
202-
"last": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields=title,summary&include=author"
204+
"self": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
205+
"first": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
206+
"last": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author"
203207
},
204208
"data": [
205209
{

docs/usage/errors.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ From a controller method:
1010
return Conflict(new Error(HttpStatusCode.Conflict)
1111
{
1212
Title = "Target resource was modified by another user.",
13-
Detail = $"User {userName} changed the {resourceField} field on the {resourceName} resource."
13+
Detail = $"User {userName} changed the {resourceField} field on {resourceName} resource."
1414
});
1515
```
1616

@@ -20,7 +20,7 @@ From other code:
2020
throw new JsonApiException(new Error(HttpStatusCode.Conflict)
2121
{
2222
Title = "Target resource was modified by another user.",
23-
Detail = $"User {userName} changed the {resourceField} field on the {resourceName} resource."
23+
Detail = $"User {userName} changed the {resourceField} field on {resourceName} resource."
2424
});
2525
```
2626

@@ -69,18 +69,22 @@ public class CustomExceptionHandler : ExceptionHandler
6969
return base.GetLogMessage(exception);
7070
}
7171

72-
protected override ErrorDocument CreateErrorDocument(Exception exception)
72+
protected override IReadOnlyList<ErrorObject> CreateErrorResponse(Exception exception)
7373
{
7474
if (exception is ProductOutOfStockException productOutOfStock)
7575
{
76-
return new ErrorDocument(new Error(HttpStatusCode.Conflict)
76+
return new[]
7777
{
78-
Title = "Product is temporarily available.",
79-
Detail = $"Product {productOutOfStock.ProductId} cannot be ordered at the moment."
80-
});
78+
new Error(HttpStatusCode.Conflict)
79+
{
80+
Title = "Product is temporarily available.",
81+
Detail = $"Product {productOutOfStock.ProductId} " +
82+
"cannot be ordered at the moment."
83+
}
84+
};
8185
}
8286

83-
return base.CreateErrorDocument(exception);
87+
return base.CreateErrorResponse(exception);
8488
}
8589
}
8690

docs/usage/extensibility/services.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public class TodoItemService : JsonApiResourceService<TodoItem, int>
1919
IQueryLayerComposer queryLayerComposer, IPaginationContext paginationContext,
2020
IJsonApiOptions options, ILoggerFactory loggerFactory, IJsonApiRequest request,
2121
IResourceChangeTracker<TodoItem> resourceChangeTracker,
22-
IResourceDefinitionAccessor resourceDefinitionAccessor)
22+
IResourceDefinitionAccessor resourceDefinitionAccessor,
23+
INotificationService notificationService)
2324
: base(repositoryAccessor, queryLayerComposer, paginationContext, options,
2425
loggerFactory, request, resourceChangeTracker, resourceDefinitionAccessor)
2526
{
@@ -121,7 +122,7 @@ IResourceService
121122
In order to take advantage of these interfaces you first need to register the service for each implemented interface.
122123

123124
```c#
124-
public class ArticleService : ICreateService<Article>, IDeleteService<Article>
125+
public class ArticleService : ICreateService<Article, int>, IDeleteService<Article, int>
125126
{
126127
// ...
127128
}
@@ -130,8 +131,8 @@ public class Startup
130131
{
131132
public void ConfigureServices(IServiceCollection services)
132133
{
133-
services.AddScoped<ICreateService<Article>, ArticleService>();
134-
services.AddScoped<IDeleteService<Article>, ArticleService>();
134+
services.AddScoped<ICreateService<Article, int>, ArticleService>();
135+
services.AddScoped<IDeleteService<Article, int>, ArticleService>();
135136
}
136137
}
137138
```

docs/usage/meta.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ Global metadata can be added to the root of the response document by registering
88
This is useful if you need access to other registered services to build the meta object.
99

1010
```c#
11+
#nullable enable
12+
1113
// In Startup.ConfigureServices
1214
services.AddSingleton<IResponseMeta, CopyrightResponseMeta>();
1315

1416
public sealed class CopyrightResponseMeta : IResponseMeta
1517
{
16-
public IReadOnlyDictionary<string, object> GetMeta()
18+
public IReadOnlyDictionary<string, object?> GetMeta()
1719
{
18-
return new Dictionary<string, object>
20+
return new Dictionary<string, object?>
1921
{
2022
["copyright"] = "Copyright (C) 2002 Umbrella Corporation.",
2123
["authors"] = new[] { "Alice", "Red Queen" }
@@ -42,21 +44,23 @@ public sealed class CopyrightResponseMeta : IResponseMeta
4244
Resource-specific metadata can be added by implementing `IResourceDefinition<TResource, TId>.GetMeta` (or overriding it on `JsonApiResourceDefinition<TResource, TId>`):
4345

4446
```c#
47+
#nullable enable
48+
4549
public class PersonDefinition : JsonApiResourceDefinition<Person, int>
4650
{
4751
public PersonDefinition(IResourceGraph resourceGraph)
4852
: base(resourceGraph)
4953
{
5054
}
5155

52-
public override IReadOnlyDictionary<string, object> GetMeta(Person person)
56+
public override IReadOnlyDictionary<string, object?>? GetMeta(Person person)
5357
{
5458
if (person.IsEmployee)
5559
{
56-
return new Dictionary<string, object>
60+
return new Dictionary<string, object?>
5761
{
5862
["notice"] = "Check our intranet at http://www.example.com/employees/" +
59-
person.StringId + " for personal details."
63+
$"{person.StringId} for personal details."
6064
};
6165
}
6266

docs/usage/options.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,19 @@ options.ValidateModelState = true;
115115
```
116116

117117
```c#
118+
#nullable enable
119+
118120
public class Person : Identifiable<int>
119121
{
120122
[Attr]
121-
[Required]
122123
[MinLength(3)]
123-
public string FirstName { get; set; }
124+
public string FirstName { get; set; } = null!;
124125

126+
[Attr]
125127
[Required]
126-
public LoginAccount Account : get; set; }
128+
public int? Age { get; set; }
129+
130+
[HasOne]
131+
public LoginAccount Account { get; set; } = null!;
127132
}
128133
```

docs/usage/resource-graph.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public void ConfigureServices(IServiceCollection services)
6565
{
6666
services.AddJsonApi(resources: builder =>
6767
{
68-
builder.Add<Person>();
68+
builder.Add<Person, int>();
6969
});
7070
}
7171
```
@@ -78,7 +78,7 @@ The public resource name is exposed through the `type` member in the JSON:API pa
7878
```c#
7979
services.AddJsonApi(resources: builder =>
8080
{
81-
builder.Add<Person>(publicName: "people");
81+
builder.Add<Person, int>(publicName: "people");
8282
});
8383
```
8484

0 commit comments

Comments
 (0)