Skip to content

Merge master into openapi #1086

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

Merged
merged 8 commits into from
Sep 20, 2021
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 5 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
<SwashbuckleVersion>6.2.*</SwashbuckleVersion>
<JsonApiDotNetCoreVersionPrefix>4.2.0</JsonApiDotNetCoreVersionPrefix>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
<WarningLevel>9999</WarningLevel>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" PrivateAssets="All" />
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.6.0" PrivateAssets="All" />
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.7.0" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CSharpGuidelinesAnalyzer.config" Visible="False" />
</ItemGroup>

Expand All @@ -23,11 +24,11 @@

<!-- Test Project Dependencies -->
<PropertyGroup>
<BogusVersion>33.0.2</BogusVersion>
<BogusVersion>33.1.1</BogusVersion>
<CoverletVersion>3.1.0</CoverletVersion>
<FluentAssertionsVersion>5.10.3</FluentAssertionsVersion>
<FluentAssertionsVersion>6.1.0</FluentAssertionsVersion>
<MoqVersion>4.16.1</MoqVersion>
<XUnitVersion>2.4.*</XUnitVersion>
<TestSdkVersion>16.10.0</TestSdkVersion>
<TestSdkVersion>16.11.0</TestSdkVersion>
</PropertyGroup>
</Project>
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ The need for breaking changes has blocked several efforts in the v4.x release, s
- [x] Tweak trace logging [#1033](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1033)
- [x] Instrumentation [#1032](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1032)
- [x] Optimized delete to-many [#1030](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1030)
- [ ] Support System.Text.Json [#664](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/664) [#999](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/999) [#233](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/233)
- [ ] Optimize IIdentifiable to ResourceObject conversion [#1028](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1028) [#1024](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1024)
- [x] Support System.Text.Json [#664](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/664) [#999](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/999) [1077](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1077) [1078](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1078)
- [ ] Optimize IIdentifiable to ResourceObject conversion [#1028](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1028) [#1024](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1024) [#233](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/233)
- [ ] Nullable reference types [#1029](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1029)

Aside from the list above, we have interest in the following topics. It's too soon yet to decide whether they'll make it into v5.x or in a later major version.
Expand Down
5 changes: 1 addition & 4 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ for:
only:
- image: Visual Studio 2019
services:
- postgresql134
# https://help.appveyor.com/discussions/problems/30239-postgres-fails-to-connect-after-version-change
init:
- net start postgresql-x64-13
- postgresql13
# REF: https://github.com/docascode/docfx-seed/blob/master/appveyor.yml
before_build:
- pwsh: |
Expand Down
3 changes: 3 additions & 0 deletions benchmarks/DependencyFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ internal sealed class DependencyFactory
public IResourceGraph CreateResourceGraph(IJsonApiOptions options)
{
var builder = new ResourceGraphBuilder(options, NullLoggerFactory.Instance);

builder.Add<BenchmarkResource>(BenchmarkResourcePublicNames.Type);
builder.Add<SubResource>();

return builder.Build();
}
}
Expand Down
4 changes: 1 addition & 3 deletions benchmarks/Query/QueryParserBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,9 @@ private static QueryStringReader CreateQueryParameterDiscoveryForAll(IResourceGr
var sortReader = new SortQueryStringParameterReader(request, resourceGraph);
var sparseFieldSetReader = new SparseFieldSetQueryStringParameterReader(request, resourceGraph);
var paginationReader = new PaginationQueryStringParameterReader(request, resourceGraph, options);
var defaultsReader = new DefaultsQueryStringParameterReader(options);
var nullsReader = new NullsQueryStringParameterReader(options);

IQueryStringParameterReader[] readers = ArrayFactory.Create<IQueryStringParameterReader>(includeReader, filterReader, sortReader,
sparseFieldSetReader, paginationReader, defaultsReader, nullsReader);
sparseFieldSetReader, paginationReader);

return new QueryStringReader(options, queryStringAccessor, readers, NullLoggerFactory.Instance);
}
Expand Down
18 changes: 7 additions & 11 deletions benchmarks/Serialization/JsonApiDeserializerBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Text.Json;
using BenchmarkDotNet.Attributes;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Serialization.Objects;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

namespace Benchmarks.Serialization
{
// ReSharper disable once ClassCanBeSealed.Global
[MarkdownExporter]
public class JsonApiDeserializerBenchmarks
{
private static readonly string Content = JsonConvert.SerializeObject(new Document
private static readonly string RequestBody = JsonSerializer.Serialize(new
{
Data = new ResourceObject
data = new
{
Type = BenchmarkResourcePublicNames.Type,
Id = "1",
Attributes = new Dictionary<string, object>
type = BenchmarkResourcePublicNames.Type,
id = "1",
attributes = new
{
["name"] = Guid.NewGuid().ToString()
}
}
});
Expand Down Expand Up @@ -55,7 +51,7 @@ public JsonApiDeserializerBenchmarks()
[Benchmark]
public object DeserializeSimpleObject()
{
return _jsonApiDeserializer.Deserialize(Content);
return _jsonApiDeserializer.Deserialize(RequestBody);
}
}
}
2 changes: 1 addition & 1 deletion benchmarks/Serialization/JsonApiSerializerBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public JsonApiSerializerBenchmarks()
ILinkBuilder linkBuilder = new Mock<ILinkBuilder>().Object;
IIncludedResourceObjectBuilder includeBuilder = new Mock<IIncludedResourceObjectBuilder>().Object;

var resourceObjectBuilder = new ResourceObjectBuilder(resourceGraph, new ResourceObjectBuilderSettings());
var resourceObjectBuilder = new ResourceObjectBuilder(resourceGraph, options);

IResourceDefinitionAccessor resourceDefinitionAccessor = new Mock<IResourceDefinitionAccessor>().Object;

Expand Down
2 changes: 1 addition & 1 deletion docs/request-examples/012_PATCH_Book.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ curl -s -f http://localhost:14141/api/books/1 `
-d '{
\"data\": {
\"type\": \"books\",
\"id\": "1",
\"id\": \"1\",
\"attributes\": {
\"publishYear\": 1820
}
Expand Down
21 changes: 10 additions & 11 deletions docs/usage/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,26 @@ To limit the maximum depth of nested includes, use `MaximumIncludeDepth`. This i
options.MaximumIncludeDepth = 1;
```

## Custom Serializer Settings
## Customize Serializer options

We use [Newtonsoft.Json](https://www.newtonsoft.com/json) for all serialization needs.
If you want to change the default serializer settings, you can:
We use [System.Text.Json](https://www.nuget.org/packages/System.Text.Json) for all serialization needs.
If you want to change the default serializer options, you can:

```c#
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.Converters.Add(new StringEnumConverter());
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
```

The default naming convention (as used in the routes and resource/attribute/relationship names) is also determined here, and can be changed (default is camel-case):

```c#
options.SerializerSettings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new KebabCaseNamingStrategy()
};
// Use Pascal case
options.SerializerOptions.PropertyNamingPolicy = null;
options.SerializerOptions.DictionaryKeyPolicy = null;
```

Because we copy resource properties into an intermediate object before serialization, Newtonsoft.Json annotations on properties are ignored.
Because we copy resource properties into an intermediate object before serialization, JSON annotations such as `[JsonPropertyName]` and `[JsonIgnore]` on `[Attr]` properties are ignored.


## Enable ModelState Validation
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/resource-graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@ public class MyModel : Identifiable
}
```

The default naming convention can be changed in [options](~/usage/options.md#custom-serializer-settings).
The default naming convention can be changed in [options](~/usage/options.md#customize-serializer-options).
15 changes: 8 additions & 7 deletions docs/usage/resources/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class Person : Identifiable

There are two ways the exposed attribute name is determined:

1. Using the configured [naming convention](~/usage/options.md#custom-serializer-settings).
1. Using the configured [naming convention](~/usage/options.md#customize-serializer-options).

2. Individually using the attribute's constructor.
```c#
Expand Down Expand Up @@ -88,9 +88,9 @@ public class Person : Identifiable
## Complex Attributes

Models may contain complex attributes.
Serialization of these types is done by [Newtonsoft.Json](https://www.newtonsoft.com/json),
so you should use their APIs to specify serialization formats.
You can also use global options to specify `JsonSerializer` configuration.
Serialization of these types is done by [System.Text.Json](https://www.nuget.org/packages/System.Text.Json),
so you should use their APIs to specify serialization format.
You can also use [global options](~/usage/options.md#customize-serializer-options) to control the `JsonSerializer` behavior.

```c#
public class Foo : Identifiable
Expand All @@ -101,7 +101,8 @@ public class Foo : Identifiable

public class Bar
{
[JsonProperty("compound-member")]
[JsonPropertyName("compound-member")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string CompoundMember { get; set; }
}
```
Expand All @@ -121,13 +122,13 @@ public class Foo : Identifiable
{
get
{
return Bar == null ? "{}" : JsonConvert.SerializeObject(Bar);
return Bar == null ? "{}" : JsonSerializer.Serialize(Bar);
}
set
{
Bar = string.IsNullOrWhiteSpace(value)
? null
: JsonConvert.DeserializeObject<Bar>(value);
: JsonSerializer.Deserialize<Bar>(value);
}
}
}
Expand Down
28 changes: 27 additions & 1 deletion docs/usage/resources/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,37 @@ public class Person : Identifiable

The left side of this relationship is of type `Person` (public name: "persons") and the right side is of type `TodoItem` (public name: "todoItems").

## HasManyThrough

_removed since v5.0_

Earlier versions of Entity Framework Core (up to v5) [did not support](https://github.com/aspnet/EntityFrameworkCore/issues/1368) many-to-many relationships without a join entity.
For this reason, earlier versions of JsonApiDotNetCore filled this gap by allowing applications to declare a relationship as `HasManyThrough`,
which would expose the relationship to the client the same way as any other `HasMany` relationship.
However, under the covers it would use the join type and Entity Framework Core's APIs to get and set the relationship.

```c#
public class Article : Identifiable
{
// tells Entity Framework Core to ignore this property
[NotMapped]

// tells JsonApiDotNetCore to use the join table below
[HasManyThrough(nameof(ArticleTags))]
public ICollection<Tag> Tags { get; set; }

// this is the Entity Framework Core navigation to the join table
public ICollection<ArticleTag> ArticleTags { get; set; }
}
```

The left side of this relationship is of type `Article` (public name: "articles") and the right side is of type `Tag` (public name: "tags").

## Name

There are two ways the exposed relationship name is determined:

1. Using the configured [naming convention](~/usage/options.md#custom-serializer-settings).
1. Using the configured [naming convention](~/usage/options.md#customize-serializer-options).

2. Individually using the attribute's constructor.
```c#
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ The exposed name of the resource ([which can be customized](~/usage/resource-gra

### Non-JSON:API controllers

If a controller does not inherit from `JsonApiController<TResource>`, the [configured naming convention](~/usage/options.md#custom-serializer-settings) is applied to the name of the controller.
If a controller does not inherit from `JsonApiController<TResource>`, the [configured naming convention](~/usage/options.md#customize-serializer-options) is applied to the name of the controller.

```c#
public class OrderLineController : ControllerBase
Expand Down
4 changes: 2 additions & 2 deletions src/Examples/GettingStarted/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchBrowser": true,
"launchUrl": "api/people",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Kestrel": {
"commandName": "Project",
"launchBrowser": false,
"launchBrowser": true,
"launchUrl": "api/people",
"applicationUrl": "http://localhost:14141",
"environmentVariables": {
Expand Down
3 changes: 1 addition & 2 deletions src/Examples/GettingStarted/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;

namespace GettingStarted
{
Expand All @@ -21,7 +20,7 @@ public void ConfigureServices(IServiceCollection services)
options.Namespace = "api";
options.UseRelativeLinks = true;
options.IncludeTotalResourceCount = true;
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerOptions.WriteIndented = true;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ public async Task<IActionResult> PostAsync()
return BadRequest("Please send your name.");
}

string result = "Hello, " + name;
string result = $"Hello, {name}";
return Ok(result);
}

[HttpPut]
public IActionResult Put([FromBody] string name)
{
string result = "Hi, " + name;
string result = $"Hi, {name}";
return Ok(result);
}

[HttpPatch]
public IActionResult Patch(string name)
{
string result = "Good day, " + name;
string result = $"Good day, {name}";
return Ok(result);
}

Expand Down
7 changes: 3 additions & 4 deletions src/Examples/JsonApiDotNetCoreExample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Diagnostics;
using JsonApiDotNetCore.OpenApi;
Expand All @@ -10,8 +11,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace JsonApiDotNetCoreExample
{
Expand Down Expand Up @@ -55,8 +54,8 @@ public void ConfigureServices(IServiceCollection services)
options.UseRelativeLinks = true;
options.ValidateModelState = true;
options.IncludeTotalResourceCount = true;
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerSettings.Converters.Add(new StringEnumConverter());
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
#if DEBUG
options.IncludeExceptionStackTraceInErrors = true;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="$(SwashbuckleVersion)" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="$(SwashbuckleVersion)" />
</ItemGroup>
</Project>
Loading