From 4d1dd82caa3599fb48954a53396de2cd7c023218 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:37:18 +0200 Subject: [PATCH 01/14] Increment version to 5.7.1 (used for pre-release builds from ci) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 05d57e58b..5f4e71ab7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,7 +9,7 @@ Recommended $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.7.0 + 5.7.1 pre 1 direct From c76ecdb2b64d7cdf7d92411849cdb9a13843f072 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:39:18 +0200 Subject: [PATCH 02/14] Add prerelease switch to OpenAPI docs --- docs/usage/openapi-client.md | 6 +++--- docs/usage/openapi.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md index c0717d47c..58b9ca87e 100644 --- a/docs/usage/openapi-client.md +++ b/docs/usage/openapi-client.md @@ -19,7 +19,7 @@ For C# clients, we provide an additional package that provides workarounds for b To add it to your project, run the following command: ``` -dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag +dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag --prerelease ``` # [Kiota](#tab/kiota) @@ -28,7 +28,7 @@ For C# clients, we provide an additional package that provides workarounds for b To add it to your project, run the following command: ``` -dotnet add package JsonApiDotNetCore.OpenApi.Client.Kiota +dotnet add package JsonApiDotNetCore.OpenApi.Client.Kiota --prerelease ``` --- @@ -60,7 +60,7 @@ The following steps describe how to generate and use a JSON:API client in C#, co 1. Add our client package to your project: ``` - dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag + dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag --prerelease ``` 1. Add code that calls one of your JSON:API endpoints. diff --git a/docs/usage/openapi.md b/docs/usage/openapi.md index e49f120a1..ae3a0b68e 100644 --- a/docs/usage/openapi.md +++ b/docs/usage/openapi.md @@ -15,7 +15,7 @@ provides OpenAPI support for JSON:API by integrating with [Swashbuckle](https:// 1. Install the `JsonApiDotNetCore.OpenApi.Swashbuckle` NuGet package: ``` - dotnet add package JsonApiDotNetCore.OpenApi.Swashbuckle + dotnet add package JsonApiDotNetCore.OpenApi.Swashbuckle --prerelease ``` > [!NOTE] From 4da3f25ad491494455f6b4eee813adf7cc28e035 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 22 Apr 2025 08:39:03 +0200 Subject: [PATCH 03/14] Reorganize README, documentation updates (#1718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reorganize README (fix #1567) * Update README.md Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Documentation updates * Reverse version table and update * Use collapsed section for JSON response --------- Co-authored-by: Grégoire --- LICENSE | 1 + PackageReadme.md | 4 +- README.md | 260 ++++++++++++++---- docs/home/index.html | 26 +- .../JsonApiDotNetCore.Annotations.csproj | 2 +- ...onApiDotNetCore.OpenApi.Swashbuckle.csproj | 2 +- .../JsonApiDotNetCore.SourceGenerators.csproj | 2 +- .../JsonApiDotNetCore.csproj | 2 +- 8 files changed, 221 insertions(+), 78 deletions(-) diff --git a/LICENSE b/LICENSE index 9bab1d270..c49362c46 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2017 Jared Nance +Copyright (c) 2020 Bart Koelman MIT License diff --git a/PackageReadme.md b/PackageReadme.md index a6d0017f6..5bb1ac534 100644 --- a/PackageReadme.md +++ b/PackageReadme.md @@ -1,5 +1,5 @@ -A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/). +A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using ASP.NET Core and Entity Framework Core. Includes support for the [Atomic Operations](https://jsonapi.org/ext/atomic/) extension. -The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy. +The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features, such as sorting, filtering, pagination, sparse fieldset selection, and side-loading related resources. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy. For more information, visit [www.jsonapi.net](https://www.jsonapi.net/). diff --git a/README.md b/README.md index d01f0b2dc..af29c1a68 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,207 @@ # JsonApiDotNetCore -A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/). [![Build](https://github.com/json-api-dotnet/JsonApiDotNetCore/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/json-api-dotnet/JsonApiDotNetCore/actions/workflows/build.yml?query=branch%3Amaster) [![Coverage](https://codecov.io/gh/json-api-dotnet/JsonApiDotNetCore/branch/master/graph/badge.svg?token=pn036tWV8T)](https://codecov.io/gh/json-api-dotnet/JsonApiDotNetCore) [![NuGet](https://img.shields.io/nuget/v/JsonApiDotNetCore.svg)](https://www.nuget.org/packages/JsonApiDotNetCore/) +[![GitHub License](https://img.shields.io/github/license/json-api-dotnet/JsonApiDotNetCore)](LICENSE) [![Chat](https://badges.gitter.im/json-api-dotnet-core/Lobby.svg)](https://gitter.im/json-api-dotnet-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![FIRST-TIMERS](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://www.firsttimersonly.com/) -The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy. +A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using ASP.NET Core and Entity Framework Core. Includes support for the [Atomic Operations](https://jsonapi.org/ext/atomic/) extension. -## Getting Started +The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features, such as sorting, filtering, pagination, sparse fieldset selection, and side-loading related resources. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy. -These are some steps you can take to help you understand what this project is and how you can use it: +> [!NOTE] +> OpenAPI support is now [available](https://www.jsonapi.net/usage/openapi.html), currently in preview. Give it a try! -### About -- [What is JSON:API and why should I use it?](https://nordicapis.com/the-benefits-of-using-json-api/) (blog, 2017) -- [Pragmatic JSON:API Design](https://www.youtube.com/watch?v=3jBJOga4e2Y) (video, 2017) -- [JSON:API and JsonApiDotNetCore](https://www.youtube.com/watch?v=79Oq0HOxyeI) (video, 2021) -- [JsonApiDotNetCore Release 4.0](https://dev.to/wunki/getting-started-5dkl) (blog, 2020) -- [JSON:API, .Net Core, EmberJS](https://youtu.be/KAMuo6K7VcE) (video, 2017) -- [Embercasts: Full Stack Ember with ASP.NET Core](https://www.embercasts.com/course/full-stack-ember-with-dotnet/watch/whats-in-this-course-cs) (paid course, 2017) +## Getting started -### Official documentation -- [The JSON:API specification](https://jsonapi.org/format/) -- [JsonApiDotNetCore website](https://www.jsonapi.net/) -- [Roadmap](ROADMAP.md) +The following steps describe how to create a JSON:API project. -## Related Projects +1. Install the JsonApiDotNetCore package, along with your preferred Entity Framework Core provider: + ```bash + dotnet add package JsonApiDotNetCore + dotnet add package Microsoft.EntityFrameworkCore.Sqlite + ``` -- [Performance Reports](https://github.com/json-api-dotnet/PerformanceReports) -- [JsonApiDotNetCore.MongoDb](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb) -- [Ember.js Todo List App](https://github.com/json-api-dotnet/TodoListExample) +1. Declare your entities, annotated with JsonApiDotNetCore attributes: + ```c# + #nullable enable + + [Resource] + public class Person : Identifiable + { + [Attr] public string? FirstName { get; set; } + [Attr] public string LastName { get; set; } = null!; + [HasMany] public ISet Children { get; set; } = new HashSet(); + } + ``` -## Examples +1. Define your `DbContext`, seeding the database with sample data: + ```c# + public class AppDbContext(DbContextOptions options) : DbContext(options) + { + public DbSet People => Set(); + + protected override void OnConfiguring(DbContextOptionsBuilder builder) + { + builder.UseSqlite("Data Source=SampleDb.db"); + builder.UseAsyncSeeding(async (dbContext, _, cancellationToken) => + { + dbContext.Set().Add(new Person + { + FirstName = "John", + LastName = "Doe", + Children = + { + new Person + { + FirstName = "Baby", + LastName = "Doe" + } + } + }); + await dbContext.SaveChangesAsync(cancellationToken); + }); + } + } + ``` -See the [examples](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples) directory for up-to-date sample applications. There is also a [Todo List App](https://github.com/json-api-dotnet/TodoListExample) that includes a JsonApiDotNetCore API and an EmberJs client. +1. Configure Entity Framework Core and JsonApiDotNetCore in `Program.cs`: + ```c# + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddDbContext(); + builder.Services.AddJsonApi(options => + { + options.UseRelativeLinks = true; + options.IncludeTotalResourceCount = true; + }); + + var app = builder.Build(); + app.UseRouting(); + app.UseJsonApi(); + app.MapControllers(); + await CreateDatabaseAsync(app.Services); + app.Run(); + + static async Task CreateDatabaseAsync(IServiceProvider serviceProvider) + { + await using var scope = serviceProvider.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Database.EnsureDeletedAsync(); + await dbContext.Database.EnsureCreatedAsync(); + } + ``` -## Installation and Usage +1. Start your API + ```bash + dotnet run + ``` -See [our documentation](https://www.jsonapi.net/) for detailed usage. +1. Send a GET request to retrieve data: + ```bash + GET http://localhost:5000/people?filter=equals(firstName,'John')&include=children HTTP/1.1 + ``` -### Models +
+ Expand to view the JSON response + + ```json + { + "links": { + "self": "/people?filter=equals(firstName,%27John%27)&include=children", + "first": "/people?filter=equals(firstName,%27John%27)&include=children", + "last": "/people?filter=equals(firstName,%27John%27)&include=children" + }, + "data": [ + { + "type": "people", + "id": "1", + "attributes": { + "firstName": "John", + "lastName": "Doe" + }, + "relationships": { + "children": { + "links": { + "self": "/people/1/relationships/children", + "related": "/people/1/children" + }, + "data": [ + { + "type": "people", + "id": "2" + } + ] + } + }, + "links": { + "self": "/people/1" + } + } + ], + "included": [ + { + "type": "people", + "id": "2", + "attributes": { + "firstName": "Baby", + "lastName": "Doe" + }, + "relationships": { + "children": { + "links": { + "self": "/people/2/relationships/children", + "related": "/people/2/children" + } + } + }, + "links": { + "self": "/people/2" + } + } + ], + "meta": { + "total": 1 + } + } + ``` -```c# -#nullable enable +
-[Resource] -public class Article : Identifiable -{ - [Attr] - public string Name { get; set; } = null!; -} -``` +## Learn more -### Middleware +The following links explain what this project provides, why it exists, and how you can use it. -```c# -// Program.cs +### About -builder.Services.AddJsonApi(); +- [What is JSON:API and why should I use it?](https://nordicapis.com/the-benefits-of-using-json-api/) (blog, 2017) +- [Pragmatic JSON:API Design](https://www.youtube.com/watch?v=3jBJOga4e2Y) (video, 2017) +- [JSON:API and JsonApiDotNetCore](https://www.youtube.com/watch?v=79Oq0HOxyeI) (video, 2021) +- [JsonApiDotNetCore Release 4.0](https://dev.to/wunki/getting-started-5dkl) (blog, 2020) +- [JSON:API, ASP.NET Core, EmberJS](https://youtu.be/KAMuo6K7VcE) (video, 2017) +- [Embercasts: Full Stack Ember with ASP.NET Core](https://www.embercasts.com/course/full-stack-ember-with-dotnet/watch/whats-in-this-course-cs) (paid course, 2017) -// ... +### Official documentation -app.UseRouting(); -app.UseJsonApi(); -app.MapControllers(); -``` +- [JsonApiDotNetCore documentation](https://www.jsonapi.net/) +- [The JSON:API specification](https://jsonapi.org/format/) +- [JsonApiDotNetCore roadmap](ROADMAP.md) + +### Samples + +- The [examples](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples) directory provides ready-to-run sample API projects +- Many advanced use cases are covered by integration tests, which can be found [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/test/JsonApiDotNetCoreTests/IntegrationTests). + This includes topics such as batching, multi-tenancy, authorization, soft-deletion, obfuscated IDs, resource inheritance, alternate routing, custom metadata, error handling and logging. +- The [Ember.js Todo List App](https://github.com/json-api-dotnet/TodoListExample) showcases a JsonApiDotNetCore API and an Ember.js client with token authentication. + +### Related projects + +- [JsonApiDotNetCore.MongoDb](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb) +- [Ember.js Todo List App](https://github.com/json-api-dotnet/TodoListExample) +- [Performance Reports](https://github.com/json-api-dotnet/PerformanceReports) ## Compatibility @@ -76,23 +210,21 @@ See also our [versioning policy](./VERSIONING_POLICY.md). | JsonApiDotNetCore | Status | .NET | Entity Framework Core | | ----------------- | ------------ | -------- | --------------------- | -| 3.x | Stable | Core 2.x | 2.x | -| 4.x | Stable | Core 3.1 | 3.1, 5 | -| | | 5 | 5 | -| | | 6 | 5 | -| 5.0.0-5.0.2 | Stable | 6 | 6 | -| 5.0.3-5.4.0 | Stable | 6 | 6, 7 | -| | | 7 | 7 | -| 5.5+ | Stable | 6 | 6, 7 | -| | | 7 | 7 | +| master | Preview | 9 | 9 | | | | 8 | 8, 9 | -| | | 9 | 9 | -| master | Preview | 8 | 8, 9 | -| | | 9 | 9 | - -## Contributing - -Have a question, found a bug or want to submit code changes? See our [contributing guidelines](./.github/CONTRIBUTING.md). +| 5.7.0+ | Stable | 9 | 9 | +| | | 8 | 8, 9 | +| 5.5.0-5.6.0 | Stable | 9 | 9 | +| | | 8 | 8, 9 | +| | | 7 | 7 | +| | | 6 | 6, 7 | +| 5.0.3-5.4.0 | Stable | 7 | 7 | +| | | 6 | 6, 7 | +| 5.0.0-5.0.2 | Stable | 6 | 6 | +| 4.x | Stable | 6 | 5 | +| | | 5 | 5 | +| | | Core 3.1 | 3.1, 5 | +| 3.x | Stable | Core 2.x | 2.x | ## Trying out the latest build @@ -115,7 +247,11 @@ To try it out, follow the steps below: and retry with the `--store-password-in-clear-text` switch added. 1. Restart your IDE, open your project, and browse the list of packages from the github-json-api feed (make sure pre-release packages are included). -## Development +## Contributing + +Have a question, found a bug or want to submit code changes? See our [contributing guidelines](./.github/CONTRIBUTING.md). + +## Build from source To build the code from this repository locally, run: @@ -123,7 +259,7 @@ To build the code from this repository locally, run: dotnet build ``` -Running tests locally requires access to a PostgreSQL database. If you have docker installed, this can be propped up via: +Running tests locally requires access to a PostgreSQL database. If you have docker installed, this can started via: ```bash pwsh run-docker-postgres.ps1 @@ -143,5 +279,9 @@ pwsh Build.ps1 ## Sponsors +We are very grateful to the sponsors below, who have provided us with a no-cost license for their tools. + JetBrains Logo   Araxis Logo + +Do you like this project? Consider to [sponsor](https://github.com/sponsors/json-api-dotnet), or just reward us by giving our repository a star. diff --git a/docs/home/index.html b/docs/home/index.html index e21d52ee9..1593c915d 100644 --- a/docs/home/index.html +++ b/docs/home/index.html @@ -4,7 +4,7 @@ JsonApiDotNetCore documentation - + @@ -12,7 +12,7 @@ - + @@ -51,8 +51,8 @@

JsonApiDotNetCore

- A framework for building JSON:API compliant REST APIs using .NET Core and Entity Framework Core. - Includes support for Atomic Operations. + A framework for building JSON:API compliant REST APIs using ASP.NET Core and Entity Framework Core. + Includes support for the Atomic Operations extension.

Read more Getting started @@ -82,7 +82,9 @@

Objectives

Eliminate boilerplate

-

We strive to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination.

+

+ The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features, such as sorting, filtering, pagination, sparse fieldset selection, and side-loading related resources. +

@@ -112,7 +114,7 @@

Filtering

Sorting

-

Order resources on one or multiple attributes using the sort query string parameter

+

Order resources on multiple attributes using the sort query string parameter

@@ -223,14 +225,14 @@

Request

Response

-{
+{
   "meta": {
     "totalResources": 1
   },
   "links": {
-    "self": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
-    "first": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
-    "last": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author"
+    "self": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
+    "first": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author",
+    "last": "/articles?filter=contains(summary,'web')&sort=-lastModifiedAt&fields%5Barticles%5D=title,summary&include=author"
   },
   "data": [
     {
diff --git a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj
index 765c4231d..ed36e0797 100644
--- a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj
+++ b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj
@@ -10,7 +10,7 @@
 
   
     jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api
-    Annotations for JsonApiDotNetCore, a framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core.
+    Annotations for JsonApiDotNetCore, which is a framework for building JSON:API compliant REST APIs using ASP.NET Core and Entity Framework Core.
     json-api-dotnet
     https://www.jsonapi.net/
     MIT
diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj
index 89822ff4b..4a57ca1c8 100644
--- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj
+++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj
@@ -11,7 +11,7 @@
   
     $(VersionPrefix)-preview.$(OpenApiPreviewNumber)
     jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api;openapi;swagger;swaggerui;swashbuckle
-    A Swashbuckle integration that enables you to describe a JsonApiDotNetCore API with an OpenAPI specification.
+    Provides OpenAPI document generation for JsonApiDotNetCore APIs by using Swashbuckle.
     json-api-dotnet
     https://www.jsonapi.net/
     MIT
diff --git a/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
index 8f9e397df..db6f039bd 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
+++ b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
@@ -12,7 +12,7 @@
 
   
     jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api
-    Source generators for JsonApiDotNetCore, a framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core.
+    Source generators for JsonApiDotNetCore, which is a framework for building JSON:API compliant REST APIs using ASP.NET Core and Entity Framework Core.
     json-api-dotnet
     https://www.jsonapi.net/
     MIT
diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
index 1c1ba7ab7..d36600e87 100644
--- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
+++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
@@ -9,7 +9,7 @@
 
   
     jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api
-    A framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core. Includes support for Atomic Operations. The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection making extensibility incredibly easy.
+    A framework for building JSON:API compliant REST APIs using ASP.NET Core and Entity Framework Core. Includes support for the Atomic Operations extension. The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features, such as sorting, filtering, pagination, sparse fieldset selection, and side-loading related resources. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy.
     json-api-dotnet
     https://www.jsonapi.net/
     MIT

From ecc07591c8a90368f7408e4769e22134f6f116aa Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Wed, 23 Apr 2025 01:30:24 +0200
Subject: [PATCH 04/14] Remove outdated install note

---
 docs/usage/openapi.md | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/docs/usage/openapi.md b/docs/usage/openapi.md
index ae3a0b68e..83f3ce8a3 100644
--- a/docs/usage/openapi.md
+++ b/docs/usage/openapi.md
@@ -18,10 +18,6 @@ provides OpenAPI support for JSON:API by integrating with [Swashbuckle](https://
     dotnet add package JsonApiDotNetCore.OpenApi.Swashbuckle --prerelease
     ```
 
-    > [!NOTE]
-    > Because this package is still experimental, it's not yet available on NuGet.
-    > Use the steps [here](https://github.com/json-api-dotnet/JsonApiDotNetCore?tab=readme-ov-file#trying-out-the-latest-build) to install.
-
 2.  Add the JSON:API support to your `Program.cs` file.
 
     ```c#

From 3ad189de365d506b37562dbbd9829b637bee44eb Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Wed, 23 Apr 2025 05:53:05 +0200
Subject: [PATCH 05/14] Publish packages to feedz.io (#1719)

* Publish packages to feedz.io

* Remove duplicate warning suppression

* Update feed instructions in README
---
 .github/workflows/build.yml |  8 ++++++++
 Directory.Build.props       |  1 -
 README.md                   | 25 +++++++++++--------------
 3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7ee1333bd..bc38da9f3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -270,6 +270,14 @@ jobs:
       run: |
         dotnet nuget add source --username 'json-api-dotnet' --password "$env:GITHUB_TOKEN" --store-password-in-clear-text --name 'github' 'https://nuget.pkg.github.com/json-api-dotnet/index.json'
         dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:GITHUB_TOKEN" --source 'github'
+    - name: Publish to feedz.io
+      if: github.event_name == 'push' || github.event_name == 'release'
+      env:
+        FEEDZ_IO_API_KEY: ${{ secrets.FEEDZ_IO_API_KEY }}
+      shell: pwsh
+      run: |
+        dotnet nuget add source --name 'feedz-io' 'https://f.feedz.io/json-api-dotnet/jsonapidotnetcore/nuget/index.json'
+        dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:FEEDZ_IO_API_KEY" --source 'feedz-io'
     - name: Publish documentation
       if: github.event_name == 'push' && github.ref == 'refs/heads/master'
       uses: peaceiris/actions-gh-pages@v4
diff --git a/Directory.Build.props b/Directory.Build.props
index 5f4e71ab7..425f6320c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -13,7 +13,6 @@
     pre
     1
     direct
-    $(NoWarn);NETSDK1215
   
 
   
diff --git a/README.md b/README.md
index af29c1a68..fb58acf05 100644
--- a/README.md
+++ b/README.md
@@ -228,24 +228,21 @@ See also our [versioning policy](./VERSIONING_POLICY.md).
 
 ## Trying out the latest build
 
-After each commit to the master branch, a new pre-release NuGet package is automatically published to [GitHub Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry).
+After each commit to the master branch, a new pre-release NuGet package is automatically published to [feedz.io](https://feedz.io/docs/package-types/nuget).
 To try it out, follow the steps below:
 
-1. [Create a Personal Access Token (classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with at least `read:packages` scope.
-1. Add our package source to your local user-specific `nuget.config` file by running:
-   ```bash
-   dotnet nuget add source https://nuget.pkg.github.com/json-api-dotnet/index.json --name github-json-api --username YOUR-GITHUB-USERNAME --password YOUR-PAT-CLASSIC
+1. Create a `nuget.config` file in the same directory as your .sln file, with the following contents:
+   ```xml
+   
+   
+     
+       
+       
+     
+   
    ```
-   In the command above:
-   - Replace YOUR-GITHUB-USERNAME with the username you use to login your GitHub account.
-   - Replace YOUR-PAT-CLASSIC with the token your created above.
 
-   :warning: If the above command doesn't give you access in the next step, remove the package source by running:
-   ```bash
-   dotnet nuget remove source github-json-api
-   ```
-   and retry with the `--store-password-in-clear-text` switch added.
-1. Restart your IDE, open your project, and browse the list of packages from the github-json-api feed (make sure pre-release packages are included).
+1. In your IDE, browse the list of packages from the `json-api-dotnet` feed. Make sure pre-release packages are included in the list.
 
 ## Contributing
 

From a0877ab8ab894a23ca68f2112480f7e5c653adee Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Fri, 25 Apr 2025 01:11:13 +0200
Subject: [PATCH 06/14] Property detect collection element type for custom
 collection types that have no or multiple type parameters (#1720)

---
 .../CollectionConverter.cs                    |  15 ++-
 .../Configuration/ResourceGraphBuilder.cs     |  12 +-
 .../CollectionConverterTests.cs               | 104 ++++++++++++++++++
 3 files changed, 118 insertions(+), 13 deletions(-)
 create mode 100644 test/JsonApiDotNetCoreTests/UnitTests/TypeConversion/CollectionConverterTests.cs

diff --git a/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs b/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs
index cbf834b63..683e34764 100644
--- a/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs
+++ b/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs
@@ -104,17 +104,26 @@ public IReadOnlyCollection ExtractResources(object? value)
     /// 
     public Type? FindCollectionElementType(Type? type)
     {
-        if (type is { IsGenericType: true, GenericTypeArguments.Length: 1 })
+        if (type != null)
         {
-            if (type.IsOrImplementsInterface())
+            Type? enumerableClosedType = IsEnumerableClosedType(type) ? type : null;
+            enumerableClosedType ??= type.GetInterfaces().FirstOrDefault(IsEnumerableClosedType);
+
+            if (enumerableClosedType != null)
             {
-                return type.GenericTypeArguments[0];
+                return enumerableClosedType.GenericTypeArguments[0];
             }
         }
 
         return null;
     }
 
+    private static bool IsEnumerableClosedType(Type type)
+    {
+        bool isClosedType = type is { IsGenericType: true, ContainsGenericParameters: false };
+        return isClosedType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
+    }
+
     /// 
     /// Indicates whether a  instance can be assigned to the specified type, for example:
     ///  GetEagerLoads(Type resourceClrTyp
                 continue;
             }
 
-            Type innerType = TypeOrElementType(property.PropertyType);
-            eagerLoad.Children = GetEagerLoads(innerType, recursionDepth + 1);
+            Type rightType = CollectionConverter.Instance.FindCollectionElementType(property.PropertyType) ?? property.PropertyType;
+            eagerLoad.Children = GetEagerLoads(rightType, recursionDepth + 1);
             eagerLoad.Property = property;
 
             eagerLoads.Add(eagerLoad);
@@ -459,14 +459,6 @@ private static void AssertNoInfiniteRecursion(int recursionDepth)
         }
     }
 
-    private Type TypeOrElementType(Type type)
-    {
-        Type[] interfaces = type.GetInterfaces().Where(@interface => @interface.IsGenericType && @interface.GetGenericTypeDefinition() == typeof(IEnumerable<>))
-            .ToArray();
-
-        return interfaces.Length == 1 ? interfaces.Single().GenericTypeArguments[0] : type;
-    }
-
     private string FormatResourceName(Type resourceClrType)
     {
         var formatter = new ResourceNameFormatter(_options.SerializerOptions.PropertyNamingPolicy);
diff --git a/test/JsonApiDotNetCoreTests/UnitTests/TypeConversion/CollectionConverterTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/TypeConversion/CollectionConverterTests.cs
new file mode 100644
index 000000000..1f679912b
--- /dev/null
+++ b/test/JsonApiDotNetCoreTests/UnitTests/TypeConversion/CollectionConverterTests.cs
@@ -0,0 +1,104 @@
+using FluentAssertions;
+using JsonApiDotNetCore;
+using Xunit;
+
+namespace JsonApiDotNetCoreTests.UnitTests.TypeConversion;
+
+public sealed class CollectionConverterTests
+{
+    [Fact]
+    public void Finds_element_type_for_generic_list()
+    {
+        // Arrange
+        Type sourceType = typeof(List);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().Be();
+    }
+
+    [Fact]
+    public void Finds_element_type_for_generic_enumerable()
+    {
+        // Arrange
+        Type sourceType = typeof(IEnumerable);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().Be();
+    }
+
+    [Fact]
+    public void Finds_element_type_for_custom_generic_collection_with_multiple_type_parameters()
+    {
+        // Arrange
+        Type sourceType = typeof(CustomCollection);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().Be();
+    }
+
+    [Fact]
+    public void Finds_element_type_for_custom_non_generic_collection()
+    {
+        // Arrange
+        Type sourceType = typeof(CustomCollectionOfIntString);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().Be();
+    }
+
+    [Fact]
+    public void Finds_no_element_type_for_non_generic_type()
+    {
+        // Arrange
+        Type sourceType = typeof(int);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().BeNull();
+    }
+
+    [Fact]
+    public void Finds_no_element_type_for_non_collection_generic_type()
+    {
+        // Arrange
+        Type sourceType = typeof(Tuple);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().BeNull();
+    }
+
+    [Fact]
+    public void Finds_no_element_type_for_unbound_generic_type()
+    {
+        // Arrange
+        Type sourceType = typeof(List<>);
+
+        // Act
+        Type? elementType = CollectionConverter.Instance.FindCollectionElementType(sourceType);
+
+        // Assert
+        elementType.Should().BeNull();
+    }
+
+    // ReSharper disable once UnusedTypeParameter
+    private class CustomCollection : List;
+
+    private sealed class CustomCollectionOfIntString : CustomCollection;
+}

From ba20f0f5d922b4ea4cf108677cd0d43c1b017c9f Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Fri, 25 Apr 2025 02:10:54 +0200
Subject: [PATCH 07/14] Fixed: build error after adding reference to
 JADNC.Kiota package (#1721)

* Fixed: build error after adding reference to JADNC.Kiota package, without any kiota references in project file yet

* Increment OpenAPI preview number
---
 Directory.Build.props                                         | 2 +-
 .../Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets      | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Directory.Build.props b/Directory.Build.props
index 425f6320c..7fc6d42c5 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -11,7 +11,7 @@
     $(MSBuildThisFileDirectory)tests.runsettings
     5.7.1
     pre
-    1
+    2
     direct
   
 
diff --git a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets
index 72b32aa7a..44586a10a 100644
--- a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets
+++ b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets
@@ -149,8 +149,8 @@
   
 
   
-  
+  
     
       <_WildcardGroup Include="%2A%2A/%2A.cs">
         %(KiotaReference._NonEmptyOutputPath)

From 6f9861b80f0e7e722cc36c962afad219502ebde6 Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Fri, 25 Apr 2025 04:26:51 +0200
Subject: [PATCH 08/14] Use https in external links (docs and schema refs)
 (#1722)

---
 .github/ISSUE_TEMPLATE/question.md                            | 2 +-
 docs/usage/meta.md                                            | 4 ++--
 src/Examples/DapperExample/Properties/launchSettings.json     | 2 +-
 .../DatabasePerTenantExample/Properties/launchSettings.json   | 2 +-
 src/Examples/GettingStarted/Properties/launchSettings.json    | 2 +-
 src/Examples/GettingStarted/README.md                         | 3 +--
 .../JsonApiDotNetCoreExample/Properties/launchSettings.json   | 2 +-
 .../MultiDbContextExample/Properties/launchSettings.json      | 2 +-
 .../NoEntityFrameworkExample/Properties/launchSettings.json   | 2 +-
 .../OpenApiKiotaClientExample/Properties/launchSettings.json  | 2 +-
 .../OpenApiNSwagClientExample/Properties/launchSettings.json  | 2 +-
 src/Examples/ReportsExample/Properties/launchSettings.json    | 2 +-
 12 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index 3e3c1ba08..7bf89d4e4 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -8,7 +8,7 @@ assignees: ''
 ---
 
 
 
 #### SUMMARY
diff --git a/docs/usage/meta.md b/docs/usage/meta.md
index a115e2574..674d39413 100644
--- a/docs/usage/meta.md
+++ b/docs/usage/meta.md
@@ -60,7 +60,7 @@ public class PersonDefinition : JsonApiResourceDefinition
         {
             return new Dictionary
             {
-                ["notice"] = "Check our intranet at http://www.example.com/employees/" +
+                ["notice"] = "Check our intranet at https://www.example.com/employees/" +
                     $"{person.StringId} for personal details."
             };
         }
@@ -80,7 +80,7 @@ public class PersonDefinition : JsonApiResourceDefinition
         ...
       },
       "meta": {
-        "notice": "Check our intranet at http://www.example.com/employees/1 for personal details."
+        "notice": "Check our intranet at https://www.example.com/employees/1 for personal details."
       }
     }
   ]
diff --git a/src/Examples/DapperExample/Properties/launchSettings.json b/src/Examples/DapperExample/Properties/launchSettings.json
index 137620d86..0d86e8f61 100644
--- a/src/Examples/DapperExample/Properties/launchSettings.json
+++ b/src/Examples/DapperExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/DatabasePerTenantExample/Properties/launchSettings.json b/src/Examples/DatabasePerTenantExample/Properties/launchSettings.json
index 1ab75296f..43ae84e51 100644
--- a/src/Examples/DatabasePerTenantExample/Properties/launchSettings.json
+++ b/src/Examples/DatabasePerTenantExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json
index d806502bc..304c37708 100644
--- a/src/Examples/GettingStarted/Properties/launchSettings.json
+++ b/src/Examples/GettingStarted/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/GettingStarted/README.md b/src/Examples/GettingStarted/README.md
index 8d8da60bc..4d94ffd82 100644
--- a/src/Examples/GettingStarted/README.md
+++ b/src/Examples/GettingStarted/README.md
@@ -7,8 +7,7 @@
 You can verify the project is running by checking this endpoint:
 `localhost:14141/api/people`
 
-For further documentation and implementation of a JsonApiDotNetCore Application see the documentation or GitHub page:
+For further documentation and implementation of a JsonApiDotNetCore application, see the documentation or GitHub page:
 
 Repository: https://github.com/json-api-dotnet/JsonApiDotNetCore
-
 Documentation: https://www.jsonapi.net
diff --git a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
index 82b89e184..7c4189f27 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
+++ b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/MultiDbContextExample/Properties/launchSettings.json b/src/Examples/MultiDbContextExample/Properties/launchSettings.json
index 9d3467265..2cb2d59ca 100644
--- a/src/Examples/MultiDbContextExample/Properties/launchSettings.json
+++ b/src/Examples/MultiDbContextExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json
index d1e2e0ca6..e5d1b8837 100644
--- a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json
+++ b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
diff --git a/src/Examples/OpenApiKiotaClientExample/Properties/launchSettings.json b/src/Examples/OpenApiKiotaClientExample/Properties/launchSettings.json
index afb5e5dac..142f412e8 100644
--- a/src/Examples/OpenApiKiotaClientExample/Properties/launchSettings.json
+++ b/src/Examples/OpenApiKiotaClientExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "profiles": {
     "Kestrel": {
       "commandName": "Project",
diff --git a/src/Examples/OpenApiNSwagClientExample/Properties/launchSettings.json b/src/Examples/OpenApiNSwagClientExample/Properties/launchSettings.json
index afb5e5dac..142f412e8 100644
--- a/src/Examples/OpenApiNSwagClientExample/Properties/launchSettings.json
+++ b/src/Examples/OpenApiNSwagClientExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "profiles": {
     "Kestrel": {
       "commandName": "Project",
diff --git a/src/Examples/ReportsExample/Properties/launchSettings.json b/src/Examples/ReportsExample/Properties/launchSettings.json
index 7add074ef..83e6baea4 100644
--- a/src/Examples/ReportsExample/Properties/launchSettings.json
+++ b/src/Examples/ReportsExample/Properties/launchSettings.json
@@ -1,5 +1,5 @@
 {
-  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,

From df1935512bbc92f2753a3ffdcd9192ac0cae5631 Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sat, 26 Apr 2025 02:14:31 +0200
Subject: [PATCH 09/14] Revert accidental binary breaking change

---
 .../Queries/Parsing/QueryTokenizer.cs         | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs b/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs
index 0c5f63437..0e1e7660a 100644
--- a/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs
+++ b/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs
@@ -1,3 +1,4 @@
+using System.Collections.ObjectModel;
 using System.Text;
 using JetBrains.Annotations;
 
@@ -6,17 +7,18 @@ namespace JsonApiDotNetCore.Queries.Parsing;
 [PublicAPI]
 public sealed class QueryTokenizer
 {
-    public static readonly Dictionary SingleCharacterToTokenKinds = new()
-    {
-        ['('] = TokenKind.OpenParen,
-        [')'] = TokenKind.CloseParen,
-        ['['] = TokenKind.OpenBracket,
-        [']'] = TokenKind.CloseBracket,
-        ['.'] = TokenKind.Period,
-        [','] = TokenKind.Comma,
-        [':'] = TokenKind.Colon,
-        ['-'] = TokenKind.Minus
-    };
+    public static readonly IReadOnlyDictionary SingleCharacterToTokenKinds = new ReadOnlyDictionary(
+        new Dictionary
+        {
+            ['('] = TokenKind.OpenParen,
+            [')'] = TokenKind.CloseParen,
+            ['['] = TokenKind.OpenBracket,
+            [']'] = TokenKind.CloseBracket,
+            ['.'] = TokenKind.Period,
+            [','] = TokenKind.Comma,
+            [':'] = TokenKind.Colon,
+            ['-'] = TokenKind.Minus
+        });
 
     private readonly string _source;
     private readonly StringBuilder _textBuffer = new();

From 39668c77b9d417f6293cb9818b1968f5f5b00460 Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:32:46 +0200
Subject: [PATCH 10/14] Move [NotMapped] after field attribute

---
 .../IntegrationTests/AtomicOperations/Playlist.cs           | 2 +-
 .../IntegrationTests/EagerLoading/Building.cs               | 6 +++---
 .../IntegrationTests/EagerLoading/Street.cs                 | 6 +++---
 .../IntegrationTests/ReadWrite/WorkItem.cs                  | 2 +-
 .../IntegrationTests/ReadWrite/WorkItemGroup.cs             | 2 +-
 .../ResourceInheritance/Models/AlwaysMovingTandem.cs        | 2 +-
 .../IntegrationTests/ZeroKeys/Game.cs                       | 2 +-
 7 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
index e8baf731d..0d6409e76 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
@@ -12,8 +12,8 @@ public sealed class Playlist : Identifiable
     [Attr]
     public string Name { get; set; } = null!;
 
-    [NotMapped]
     [Attr]
+    [NotMapped]
     public bool IsArchived => false;
 
     [HasMany]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
index ea3805cb2..78ab51463 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
@@ -14,12 +14,12 @@ public sealed class Building : Identifiable
     [Attr]
     public string Number { get; set; } = null!;
 
-    [NotMapped]
     [Attr]
+    [NotMapped]
     public int WindowCount => Windows.Count;
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.AllowView | AttrCapabilities.AllowChange)]
+    [NotMapped]
     public string PrimaryDoorColor
     {
         get
@@ -50,8 +50,8 @@ public string PrimaryDoorColor
         }
     }
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.AllowView)]
+    [NotMapped]
     public string? SecondaryDoorColor => SecondaryDoor?.Color;
 
     [EagerLoad]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
index db440ad85..263bb6cfd 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
@@ -12,16 +12,16 @@ public sealed class Street : Identifiable
     [Attr]
     public string Name { get; set; } = null!;
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.AllowView)]
+    [NotMapped]
     public int BuildingCount => Buildings.Count;
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.AllowView)]
+    [NotMapped]
     public int DoorTotalCount => Buildings.Sum(building => building.SecondaryDoor == null ? 1 : 2);
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.AllowView)]
+    [NotMapped]
     public int WindowTotalCount => Buildings.Sum(building => building.WindowCount);
 
     [EagerLoad]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs
index c722592e8..baf2da12c 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs
@@ -18,8 +18,8 @@ public sealed class WorkItem : Identifiable
     [Attr]
     public WorkItemPriority Priority { get; set; }
 
-    [NotMapped]
     [Attr(Capabilities = AttrCapabilities.All & ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))]
+    [NotMapped]
     public bool IsImportant
     {
         get => Priority == WorkItemPriority.High;
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs
index 7f132c899..b5b94258f 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs
@@ -15,8 +15,8 @@ public sealed class WorkItemGroup : Identifiable
     [Attr]
     public bool IsPublic { get; set; }
 
-    [NotMapped]
     [Attr]
+    [NotMapped]
     public bool IsDeprecated => !string.IsNullOrEmpty(Name) && Name.StartsWith("DEPRECATED:", StringComparison.OrdinalIgnoreCase);
 
     [HasOne]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Models/AlwaysMovingTandem.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Models/AlwaysMovingTandem.cs
index 590e05333..c90aad6ad 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Models/AlwaysMovingTandem.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Models/AlwaysMovingTandem.cs
@@ -9,8 +9,8 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance.Models;
 [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance", GenerateControllerEndpoints = JsonApiEndpoints.None)]
 public sealed class AlwaysMovingTandem : Bike
 {
-    [NotMapped]
     [Attr]
+    [NotMapped]
     public Guid LocationToken
     {
         get => Guid.NewGuid();
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs
index 55372d4b0..a983c66d7 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs
@@ -13,8 +13,8 @@ public sealed class Game : Identifiable
     [Attr]
     public string Title { get; set; } = null!;
 
-    [NotMapped]
     [Attr]
+    [NotMapped]
     public Guid SessionToken => Guid.NewGuid();
 
     [HasOne]

From 049ed69f23a3bbc20d88f5f881f09195ec34293d Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:33:01 +0200
Subject: [PATCH 11/14] Add missing null check

---
 test/TestBuildingBlocks/ServiceCollectionExtensions.cs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs
index 8e5102fa2..886a5f0fb 100644
--- a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs
+++ b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs
@@ -10,6 +10,7 @@ internal static class ServiceCollectionExtensions
     public static void ReplaceControllers(this IServiceCollection services, TestControllerProvider provider)
     {
         ArgumentNullException.ThrowIfNull(services);
+        ArgumentNullException.ThrowIfNull(provider);
 
         services.AddMvcCore().ConfigureApplicationPartManager(manager =>
         {

From 6321a1a597712d1b085e1d363a5201a6923413ca Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:33:19 +0200
Subject: [PATCH 12/14] Simplify single-line lambda

---
 .../IntegrationTests/Meta/TopLevelCountTests.cs              | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/TopLevelCountTests.cs
index 5a72d4af8..aa4eb0659 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/TopLevelCountTests.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/TopLevelCountTests.cs
@@ -83,10 +83,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
     public async Task Renders_resource_count_for_empty_collection()
     {
         // Arrange
-        await _testContext.RunOnDatabaseAsync(async dbContext =>
-        {
-            await dbContext.ClearTableAsync();
-        });
+        await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.ClearTableAsync());
 
         const string route = "/supportTickets";
 

From 7a59c74dcd8d39857f902b1efef2183dae345432 Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sat, 26 Apr 2025 23:55:23 +0200
Subject: [PATCH 13/14] Minor tweaks to improve code readability (#1723)

---
 src/JsonApiDotNetCore/Configuration/TypeLocator.cs | 10 +++++++++-
 .../CarCompositeKeyAwareRepository.cs              | 14 ++++++++++++--
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs
index 6b8c54574..768f94a98 100644
--- a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs
+++ b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs
@@ -82,8 +82,16 @@ internal sealed class TypeLocator
                 $"instead of {interfaceTypeArguments.Length}.", nameof(interfaceTypeArguments));
         }
 
-        return assembly.GetTypes().Select(type => GetContainerRegistrationFromType(type, unboundInterface, interfaceTypeArguments))
+        // @formatter:wrap_chained_method_calls chop_always
+        // @formatter:wrap_before_first_method_call true
+
+        return assembly
+            .GetTypes()
+            .Select(type => GetContainerRegistrationFromType(type, unboundInterface, interfaceTypeArguments))
             .FirstOrDefault(result => result != null);
+
+        // @formatter:wrap_before_first_method_call restore
+        // @formatter:wrap_chained_method_calls restore
     }
 
     private static (Type implementationType, Type serviceInterface)? GetContainerRegistrationFromType(Type nextType, Type unboundInterface,
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarCompositeKeyAwareRepository.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarCompositeKeyAwareRepository.cs
index 089f48790..d320c65d0 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarCompositeKeyAwareRepository.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarCompositeKeyAwareRepository.cs
@@ -39,11 +39,21 @@ private void RecursiveRewriteFilterInLayer(QueryLayer queryLayer)
 
         if (queryLayer.Selection is { IsEmpty: false })
         {
-            foreach (QueryLayer? nextLayer in queryLayer.Selection.GetResourceTypes().Select(queryLayer.Selection.GetOrCreateSelectors)
-                .SelectMany(selectors => selectors.Select(selector => selector.Value).Where(layer => layer != null)))
+            // @formatter:wrap_chained_method_calls chop_always
+            // @formatter:keep_existing_linebreaks true
+
+            foreach (QueryLayer? nextLayer in queryLayer.Selection
+                .GetResourceTypes()
+                .Select(queryLayer.Selection.GetOrCreateSelectors)
+                .SelectMany(selectors => selectors
+                    .Select(selector => selector.Value)
+                    .Where(layer => layer != null)))
             {
                 RecursiveRewriteFilterInLayer(nextLayer!);
             }
+
+            // @formatter:keep_existing_linebreaks restore
+            // @formatter:wrap_chained_method_calls restore
         }
     }
 }

From b45b3ffe5834143468a285c71065ba3e5ce177d1 Mon Sep 17 00:00:00 2001
From: Bart Koelman <10324372+bkoelman@users.noreply.github.com>
Date: Sun, 27 Apr 2025 00:25:11 +0200
Subject: [PATCH 14/14] Remove Experimental attribute from
 AddOpenApiForJsonApi, now that preview packages are deployed on NuGet (#1724)

---
 src/Examples/JsonApiDotNetCoreExample/Program.cs               | 2 --
 .../ServiceCollectionExtensions.cs                             | 2 --
 test/OpenApiTests/OpenApiStartup.cs                            | 3 ---
 3 files changed, 7 deletions(-)

diff --git a/src/Examples/JsonApiDotNetCoreExample/Program.cs b/src/Examples/JsonApiDotNetCoreExample/Program.cs
index d2677ea78..56448b271 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Program.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Program.cs
@@ -79,9 +79,7 @@ static void ConfigureServices(WebApplicationBuilder builder)
 
     using (CodeTimingSessionManager.Current.Measure("AddOpenApiForJsonApi()"))
     {
-#pragma warning disable JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
         builder.Services.AddOpenApiForJsonApi(options => options.DocumentFilter());
-#pragma warning restore JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
     }
 }
 
diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ServiceCollectionExtensions.cs
index df5247d9d..3b421c31d 100644
--- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ServiceCollectionExtensions.cs
+++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ServiceCollectionExtensions.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics.CodeAnalysis;
 using JsonApiDotNetCore.Configuration;
 using JsonApiDotNetCore.Middleware;
 using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata;
@@ -21,7 +20,6 @@ public static class ServiceCollectionExtensions
     /// 
     /// Configures OpenAPI for JsonApiDotNetCore using Swashbuckle.
     /// 
-    [Experimental("JADNC_OA_001", UrlFormat = "https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/docs/usage/openapi.md")]
     public static void AddOpenApiForJsonApi(this IServiceCollection services, Action? configureSwaggerGenOptions = null)
     {
         ArgumentNullException.ThrowIfNull(services);
diff --git a/test/OpenApiTests/OpenApiStartup.cs b/test/OpenApiTests/OpenApiStartup.cs
index ba0a5256c..e8ee0fdb2 100644
--- a/test/OpenApiTests/OpenApiStartup.cs
+++ b/test/OpenApiTests/OpenApiStartup.cs
@@ -14,10 +14,7 @@ public class OpenApiStartup : TestableStartup
     public override void ConfigureServices(IServiceCollection services)
     {
         base.ConfigureServices(services);
-
-#pragma warning disable JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
         services.AddOpenApiForJsonApi(SetupSwaggerGenAction);
-#pragma warning restore JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
     }
 
     protected override void SetJsonApiOptions(JsonApiOptions options)




pFad - Phonifier reborn



Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy