Skip to content

Write enhancements #874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion benchmarks/Serialization/JsonApiDeserializerBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.ComponentModel.Design;
using BenchmarkDotNet.Attributes;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Serialization.Objects;
Expand Down Expand Up @@ -37,7 +38,8 @@ public JsonApiDeserializerBenchmarks()
var options = new JsonApiOptions();
IResourceGraph resourceGraph = DependencyFactory.CreateResourceGraph(options);
var targetedFields = new TargetedFields();
_jsonApiDeserializer = new RequestDeserializer(resourceGraph, new ResourceFactory(new ServiceContainer()), targetedFields, new HttpContextAccessor());
var request = new JsonApiRequest();
_jsonApiDeserializer = new RequestDeserializer(resourceGraph, new ResourceFactory(new ServiceContainer()), targetedFields, new HttpContextAccessor(), request);
}

[Benchmark]
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Serialization/JsonApiSerializerBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static FieldsToSerialize CreateFieldsToSerialize(IResourceGraph resource

var accessor = new Mock<IResourceDefinitionAccessor>().Object;

return new FieldsToSerialize(resourceGraph, constraintProviders, accessor);
return new FieldsToSerialize(resourceGraph, constraintProviders, accessor, request);
}

[Benchmark]
Expand Down
2 changes: 1 addition & 1 deletion docs/internals/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Internals

The section contains overviews for the inner workings of the JsonApiDotNetCore library.
This section contains overviews for the inner workings of the JsonApiDotNetCore library.
8 changes: 4 additions & 4 deletions docs/request-examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ _Note that cURL requires "[" and "]" in URLs to be escaped._

# Writing data

### Create
### Create resource

[!code-ps[REQUEST](010_CREATE_Person.ps1)]
[!code-json[RESPONSE](010_CREATE_Person_Response.json)]

### Create with relationship
### Create resource with relationship

[!code-ps[REQUEST](011_CREATE_Book-with-Author.ps1)]
[!code-json[RESPONSE](011_CREATE_Book-with-Author_Response.json)]

### Update
### Update resource

[!code-ps[REQUEST](012_PATCH_Book.ps1)]
[!code-json[RESPONSE](012_PATCH_Book_Response.json)]

### Delete
### Delete resource

[!code-ps[REQUEST](013_DELETE_Book.ps1)]
[!code-json[RESPONSE](013_DELETE_Book_Response.json)]
17 changes: 16 additions & 1 deletion docs/usage/extensibility/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
If you want to use a data access technology other than Entity Framework Core, you can create an implementation of `IResourceRepository<TResource, TId>`.
If you only need minor changes you can override the methods defined in `EntityFrameworkCoreRepository<TResource, TId>`.

The repository should then be added to the service collection in Startup.cs.
The repository should then be registered in Startup.cs.

```c#
public void ConfigureServices(IServiceCollection services)
Expand All @@ -12,6 +12,21 @@ public void ConfigureServices(IServiceCollection services)
}
```

In v4.0 we introduced an extension method that you can use to register a resource repository on all of its JsonApiDotNetCore interfaces.
This is helpful when you implement a subset of the resource interfaces and want to register them all in one go.

Note: If you're using service discovery, this happens automatically.

```c#
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddResourceRepository<ArticleRepository>();
}
}
```

A sample implementation that performs authorization might look like this.

All of the methods in EntityFrameworkCoreRepository will use the `GetAll()` method to get the `DbSet<TResource>`, so this is a good method to apply filters such as user or tenant authorization.
Expand Down
4 changes: 3 additions & 1 deletion docs/usage/extensibility/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ IResourceService
PATCH /{id}/relationships/{relationship}
```

In order to take advantage of these interfaces you first need to inject the service for each implemented interface.
In order to take advantage of these interfaces you first need to register the service for each implemented interface.

```c#
public class ArticleService : ICreateService<Article>, IDeleteService<Article>
Expand All @@ -136,6 +136,8 @@ public class Startup
In v3.0 we introduced an extension method that you can use to register a resource service on all of its JsonApiDotNetCore interfaces.
This is helpful when you implement a subset of the resource interfaces and want to register them all in one go.

Note: If you're using service discovery, this happens automatically.

```c#
public class Startup
{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 0 additions & 3 deletions docs/usage/resources/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public class TodoItem : Identifiable<int>
}
```

The convention used to locate the foreign key property (e.g. `OwnerId`) can be changed on
the @JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_RelatedIdMapper

## HasMany

```c#
Expand Down
17 changes: 12 additions & 5 deletions docs/usage/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
## [Relationships](resources/relationships.md)
## [Resource Definitions](resources/resource-definitions.md)

# Reading data
## [Filtering](reading/filtering.md)
## [Sorting](reading/sorting.md)
## [Pagination](reading/pagination.md)
## [Sparse Fieldset Selection](reading/sparse-fieldset-selection.md)
## [Including Relationships](reading/including-relationships.md)

# Writing data
## [Creating](writing/creating.md)
## [Updating](writing/updating.md)
## [Deleting](writing/deleting.md)

# [Resource Graph](resource-graph.md)
# [Options](options.md)
# [Filtering](filtering.md)
# [Sorting](sorting.md)
# [Pagination](pagination.md)
# [Sparse Fieldset Selection](sparse-fieldset-selection.md)
# [Including Relationships](including-relationships.md)
# [Routing](routing.md)
# [Errors](errors.md)
# [Metadata](meta.md)
Expand Down
74 changes: 74 additions & 0 deletions docs/usage/writing/creating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Creating resources

A single resource can be created by sending a POST request. The next example creates a new article:

```http
POST /articles HTTP/1.1

{
"data": {
"type": "articles",
"attributes": {
"caption": "A new article!",
"url": "www.website.com"
}
}
}
```

When using client-generated IDs and only attributes from the request have changed, the server returns `204 No Content`.
Otherwise, the server returns `200 OK`, along with the updated resource and its newly assigned ID.

In both cases, a `Location` header is returned that contains the URL to the new resource.

# Creating resources with relationships

It is possible to create a new resource and establish relationships to existing resources in a single request.
The example below creates an article and sets both its owner and tags.

```http
POST /articles HTTP/1.1

{
"data": {
"type": "articles",
"attributes": {
"caption": "A new article!"
},
"relationships": {
"author": {
"data": {
"type": "person",
"id": "101"
}
},
"tags": {
"data": [
{
"type": "tag",
"id": "123"
},
{
"type": "tag",
"id": "456"
}
]
}
}
}
}
```

# Response body

POST requests can be combined with query string parameters that are normally used for reading data, such as `include` and `fields`. For example:

```http
POST /articles?include=owner&fields[owner]=firstName HTTP/1.1

{
...
}
```

After the resource has been created on the server, it is re-fetched from the database using the specified query string parameters and returned to the client.
9 changes: 9 additions & 0 deletions docs/usage/writing/deleting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Deleting resources

A single resource can be deleted using a DELETE request. The next example deletes an article:

```http
DELETE /articles/1 HTTP/1.1
```

This returns `204 No Content` if the resource was successfully deleted. Alternatively, if the resource does not exist, `404 Not Found` is returned.
137 changes: 137 additions & 0 deletions docs/usage/writing/updating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Updating resources

## Updating resource attributes

To modify the attributes of a single resource, send a PATCH request. The next example changes the article caption:

```http
POST /articles HTTP/1.1

{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"caption": "This has changed"
}
}
}
```

This preserves the values of all other unsent attributes and is called a *partial patch*.

When only the attributes that were sent in the request have changed, the server returns `204 No Content`.
But if additional attributes have changed (for example, by a database trigger that refreshes the last-modified date) the server returns `200 OK`, along with all attributes of the updated resource.

## Updating resource relationships

Besides its attributes, the relationships of a resource can be changed using a PATCH request too.
Note that all resources being assigned in a relationship must already exist.

When updating a HasMany relationship, the existing set is replaced by the new set. See below on how to add/remove resources.

The next example replaces both the owner and tags of an article.

```http
PATCH /articles/1 HTTP/1.1

{
"data": {
"type": "articles",
"id": "1",
"relationships": {
"author": {
"data": {
"type": "person",
"id": "101"
}
},
"tags": {
"data": [
{
"type": "tag",
"id": "123"
},
{
"type": "tag",
"id": "456"
}
]
}
}
}
}
```

A HasOne relationship can be cleared by setting `data` to `null`, while a HasMany relationship can be cleared by setting it to an empty array.

By combining the examples above, both attributes and relationships can be updated using a single PATCH request.

## Response body

PATCH requests can be combined with query string parameters that are normally used for reading data, such as `include` and `fields`. For example:

```http
PATCH /articles/1?include=owner&fields[owner]=firstName HTTP/1.1

{
...
}
```

After the resource has been updated on the server, it is re-fetched from the database using the specified query string parameters and returned to the client.
Note this only has an effect when `200 OK` is returned.

# Updating relationships

Although relationships can be modified along with resources (as described above), it is also possible to change a single relationship using a relationship URL.
The same rules for clearing the relationship apply. And similar to PATCH on a resource URL, updating a HasMany relationship replaces the existing set.

The next example changes just the owner of an article, by sending a PATCH request to its relationship URL.

```http
PATCH /articles/1/relationships/owner HTTP/1.1

{
"data": {
"type": "person",
"id": "101"
}
}
```

The server returns `204 No Content` when the update is successful.

## Changing HasMany relationships

The POST and DELETE verbs can be used on HasMany relationship URLs to add or remove resources to/from an existing set without replacing it.

The next example adds another tag to the existing set of tags of an article:

```http
POST /articles/1/relationships/tags HTTP/1.1

{
"data": [
{
"type": "tag",
"id": "789"
}
]
}
```

Likewise, the next example removes a single tag from the set of tags of an article:

```http
DELETE /articles/1/relationships/tags HTTP/1.1

{
"data": [
{
"type": "tag",
"id": "789"
}
]
}
```
Loading