diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 69e33b978d..3bd80e6084 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -9,7 +9,7 @@
]
},
"regitlint": {
- "version": "6.0.8",
+ "version": "6.1.1",
"commands": [
"regitlint"
]
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 5e87f135f0..4205b1ceec 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -58,8 +58,8 @@ Please follow these steps to have your contribution considered by the maintainer
We use [CSharpGuidelines](https://csharpcodingguidelines.com/) as our coding standard (with a few minor exceptions). Coding style is validated during PR build, where we inject an extra settings layer that promotes various suggestions to warning level. This ensures a high-quality codebase without interfering too much when editing code.
You can run the following [PowerShell scripts](https://github.com/PowerShell/PowerShell/releases) locally:
-- `pwsh inspectcode.ps1`: Scans the code for style violations and opens the result in your web browser.
-- `pwsh cleanupcode.ps1`: Reformats the entire codebase to match with our configured style.
+- `pwsh ./inspectcode.ps1`: Scans the code for style violations and opens the result in your web browser.
+- `pwsh ./cleanupcode.ps1 [branch-name-or-commit-hash]`: Reformats the codebase to match with our configured style, optionally only changed files since the specified branch (usually master).
Code inspection violations can be addressed in several ways, depending on the situation:
- Types that are reported to be never instantiated (because the IoC container creates them dynamically) should be decorated with `[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]`.
diff --git a/Build.ps1 b/Build.ps1
index b02e4bb7bb..69ea0f2fe4 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -40,16 +40,21 @@ function RunCleanupCode {
# When running in cibuild for a pull request, this reformats only the files changed in the PR and fails if the reformat produces changes.
if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) {
- Write-Output "Running code cleanup on changed files in pull request"
-
# In the past, we used $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT for the merge commit hash. That is the pinned hash at the time the build is enqueued.
# When a force-push happens after that, while the build hasn't yet started, this hash becomes invalid during the build, resulting in a lookup error.
- # To prevent failing the build for unobvious reasons we use HEAD, which is always the latest version.
- $mergeCommitHash = git rev-parse "HEAD"
- $targetCommitHash = git rev-parse "$env:APPVEYOR_REPO_BRANCH"
+ # To prevent failing the build for unobvious reasons we use HEAD, which is always a detached head (the PR merge result).
+
+ $headCommitHash = git rev-parse HEAD
+ CheckLastExitCode
- dotnet regitlint -s JsonApiDotNetCore.sln --print-command --disable-jb-path-hack --jb --profile='\"JADNC Full Cleanup\"' --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $mergeCommitHash -b $targetCommitHash --fail-on-diff --print-diff
+ $baseCommitHash = git rev-parse "$env:APPVEYOR_REPO_BRANCH"
CheckLastExitCode
+
+ if ($baseCommitHash -ne $headCommitHash) {
+ Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request."
+ dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff
+ CheckLastExitCode
+ }
}
}
diff --git a/Directory.Build.props b/Directory.Build.props
index c104cbdd11..b10fb859f3 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,10 +4,13 @@
6.0.*
6.0.*
6.0.*
- 4.2.*
+ 4.3.*
2.14.1
- 6.2.*
- 5.0.3
+ 6.4.*
+ 13.16.*
+ 6.0.*
+ 13.0.*
+ 5.0.4
$(MSBuildThisFileDirectory)CodingGuidelines.ruleset
9999
enable
@@ -18,7 +21,7 @@
-
+
@@ -35,7 +38,7 @@
3.1.2
- 4.18.1
- 17.2.0
+ 4.18.2
+ 17.3.1
diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings
index bd9975440c..5e593dd32d 100644
--- a/JsonApiDotNetCore.sln.DotSettings
+++ b/JsonApiDotNetCore.sln.DotSettings
@@ -57,6 +57,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$);
WARNING
WARNING
WARNING
+ WARNING
DO_NOT_SHOW
HINT
SUGGESTION
diff --git a/benchmarks/Benchmarks.csproj b/benchmarks/Benchmarks.csproj
index 4bde435c15..3958713af4 100644
--- a/benchmarks/Benchmarks.csproj
+++ b/benchmarks/Benchmarks.csproj
@@ -2,6 +2,7 @@
Exe
$(TargetFrameworkName)
+ true
@@ -9,7 +10,9 @@
-
-
+
+
+
+
diff --git a/benchmarks/Deserialization/OperationsDeserializationBenchmarks.cs b/benchmarks/Deserialization/OperationsDeserializationBenchmarks.cs
index d28684e27b..99adce73cb 100644
--- a/benchmarks/Deserialization/OperationsDeserializationBenchmarks.cs
+++ b/benchmarks/Deserialization/OperationsDeserializationBenchmarks.cs
@@ -7,6 +7,7 @@
namespace Benchmarks.Deserialization;
[MarkdownExporter]
+[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global
public class OperationsDeserializationBenchmarks : DeserializationBenchmarkBase
{
diff --git a/benchmarks/Deserialization/ResourceDeserializationBenchmarks.cs b/benchmarks/Deserialization/ResourceDeserializationBenchmarks.cs
index 23a6205bf5..e503a329bb 100644
--- a/benchmarks/Deserialization/ResourceDeserializationBenchmarks.cs
+++ b/benchmarks/Deserialization/ResourceDeserializationBenchmarks.cs
@@ -7,6 +7,7 @@
namespace Benchmarks.Deserialization;
[MarkdownExporter]
+[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global
public class ResourceDeserializationBenchmarks : DeserializationBenchmarkBase
{
diff --git a/benchmarks/QueryString/QueryStringParserBenchmarks.cs b/benchmarks/QueryString/QueryStringParserBenchmarks.cs
index efa4f12659..4218c2e3dc 100644
--- a/benchmarks/QueryString/QueryStringParserBenchmarks.cs
+++ b/benchmarks/QueryString/QueryStringParserBenchmarks.cs
@@ -1,13 +1,12 @@
using System.ComponentModel.Design;
using BenchmarkDotNet.Attributes;
+using Benchmarks.Tools;
using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.QueryStrings;
using JsonApiDotNetCore.QueryStrings.Internal;
using JsonApiDotNetCore.Resources;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging.Abstractions;
namespace Benchmarks.QueryString;
@@ -71,31 +70,9 @@ public void DescendingSort()
[Benchmark]
public void ComplexQuery()
{
- Run(100, () =>
- {
- const string queryString =
- "?filter[alt-attr-name]=abc,eq:abc&sort=-alt-attr-name&include=child&page[size]=1&fields[alt-resource-name]=alt-attr-name";
-
- _queryStringAccessor.SetQueryString(queryString);
- _queryStringReader.ReadAll(null);
- });
- }
-
- private void Run(int iterations, Action action)
- {
- for (int index = 0; index < iterations; index++)
- {
- action();
- }
- }
-
- private sealed class FakeRequestQueryStringAccessor : IRequestQueryStringAccessor
- {
- public IQueryCollection Query { get; private set; } = new QueryCollection();
+ const string queryString = "?filter[alt-attr-name]=abc,eq:abc&sort=-alt-attr-name&include=child&page[size]=1&fields[alt-resource-name]=alt-attr-name";
- public void SetQueryString(string queryString)
- {
- Query = new QueryCollection(QueryHelpers.ParseQuery(queryString));
- }
+ _queryStringAccessor.SetQueryString(queryString);
+ _queryStringReader.ReadAll(null);
}
}
diff --git a/benchmarks/Serialization/OperationsSerializationBenchmarks.cs b/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
index 7076ca5cb8..471c9604c7 100644
--- a/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
+++ b/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
@@ -9,6 +9,7 @@
namespace Benchmarks.Serialization;
[MarkdownExporter]
+[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global
public class OperationsSerializationBenchmarks : SerializationBenchmarkBase
{
diff --git a/benchmarks/Serialization/ResourceSerializationBenchmarks.cs b/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
index 12f5c2e788..a985bd5936 100644
--- a/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
+++ b/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
@@ -12,6 +12,7 @@
namespace Benchmarks.Serialization;
[MarkdownExporter]
+[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global
public class ResourceSerializationBenchmarks : SerializationBenchmarkBase
{
diff --git a/benchmarks/Serialization/SerializationBenchmarkBase.cs b/benchmarks/Serialization/SerializationBenchmarkBase.cs
index e1bcb10843..d9cfefd0b6 100644
--- a/benchmarks/Serialization/SerializationBenchmarkBase.cs
+++ b/benchmarks/Serialization/SerializationBenchmarkBase.cs
@@ -1,18 +1,14 @@
-using System.Collections.Immutable;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Benchmarks.Tools;
using JetBrains.Annotations;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Queries;
-using JsonApiDotNetCore.Queries.Expressions;
using JsonApiDotNetCore.Queries.Internal;
-using JsonApiDotNetCore.QueryStrings;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
-using JsonApiDotNetCore.Serialization.Objects;
using JsonApiDotNetCore.Serialization.Response;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging.Abstractions;
namespace Benchmarks.Serialization;
@@ -45,9 +41,9 @@ protected SerializationBenchmarkBase()
// ReSharper restore VirtualMemberCallInConstructor
var linkBuilder = new FakeLinkBuilder();
- var metaBuilder = new FakeMetaBuilder();
+ var metaBuilder = new NoMetaBuilder();
IQueryConstraintProvider[] constraintProviders = Array.Empty();
- var resourceDefinitionAccessor = new FakeResourceDefinitionAccessor();
+ var resourceDefinitionAccessor = new NeverResourceDefinitionAccessor();
var sparseFieldSetCache = new SparseFieldSetCache(constraintProviders, resourceDefinitionAccessor);
var requestQueryStringAccessor = new FakeRequestQueryStringAccessor();
@@ -122,141 +118,4 @@ public sealed class OutgoingResource : Identifiable
[HasMany]
public ISet Multi5 { get; set; } = null!;
}
-
- private sealed class FakeResourceDefinitionAccessor : IResourceDefinitionAccessor
- {
- public IImmutableSet OnApplyIncludes(ResourceType resourceType, IImmutableSet existingIncludes)
- {
- return existingIncludes;
- }
-
- public FilterExpression? OnApplyFilter(ResourceType resourceType, FilterExpression? existingFilter)
- {
- return existingFilter;
- }
-
- public SortExpression? OnApplySort(ResourceType resourceType, SortExpression? existingSort)
- {
- return existingSort;
- }
-
- public PaginationExpression? OnApplyPagination(ResourceType resourceType, PaginationExpression? existingPagination)
- {
- return existingPagination;
- }
-
- public SparseFieldSetExpression? OnApplySparseFieldSet(ResourceType resourceType, SparseFieldSetExpression? existingSparseFieldSet)
- {
- return existingSparseFieldSet;
- }
-
- public object? GetQueryableHandlerForQueryStringParameter(Type resourceClrType, string parameterName)
- {
- return null;
- }
-
- public IDictionary? GetMeta(ResourceType resourceType, IIdentifiable resourceInstance)
- {
- return null;
- }
-
- public Task OnPrepareWriteAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public Task OnSetToOneRelationshipAsync(TResource leftResource, HasOneAttribute hasOneRelationship,
- IIdentifiable? rightResourceId, WriteOperationKind writeOperation, CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.FromResult(rightResourceId);
- }
-
- public Task OnSetToManyRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
- WriteOperationKind writeOperation, CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public Task OnAddToRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
- CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public Task OnRemoveFromRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
- CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public Task OnWritingAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public Task OnWriteSucceededAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
- where TResource : class, IIdentifiable
- {
- return Task.CompletedTask;
- }
-
- public void OnDeserialize(IIdentifiable resource)
- {
- }
-
- public void OnSerialize(IIdentifiable resource)
- {
- }
- }
-
- private sealed class FakeLinkBuilder : ILinkBuilder
- {
- public TopLevelLinks GetTopLevelLinks()
- {
- return new TopLevelLinks
- {
- Self = "TopLevel:Self"
- };
- }
-
- public ResourceLinks GetResourceLinks(ResourceType resourceType, IIdentifiable resource)
- {
- return new ResourceLinks
- {
- Self = "Resource:Self"
- };
- }
-
- public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable leftResource)
- {
- return new RelationshipLinks
- {
- Self = "Relationship:Self",
- Related = "Relationship:Related"
- };
- }
- }
-
- private sealed class FakeMetaBuilder : IMetaBuilder
- {
- public void Add(IDictionary values)
- {
- }
-
- public IDictionary? Build()
- {
- return null;
- }
- }
-
- private sealed class FakeRequestQueryStringAccessor : IRequestQueryStringAccessor
- {
- public IQueryCollection Query { get; } = new QueryCollection(0);
- }
}
diff --git a/benchmarks/Tools/FakeLinkBuilder.cs b/benchmarks/Tools/FakeLinkBuilder.cs
new file mode 100644
index 0000000000..3468237507
--- /dev/null
+++ b/benchmarks/Tools/FakeLinkBuilder.cs
@@ -0,0 +1,39 @@
+using JsonApiDotNetCore.Configuration;
+using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
+using JsonApiDotNetCore.Serialization.Objects;
+using JsonApiDotNetCore.Serialization.Response;
+using Microsoft.AspNetCore.Http;
+
+namespace Benchmarks.Tools;
+
+///
+/// Renders hard-coded fake links, without depending on .
+///
+internal sealed class FakeLinkBuilder : ILinkBuilder
+{
+ public TopLevelLinks GetTopLevelLinks()
+ {
+ return new TopLevelLinks
+ {
+ Self = "TopLevel:Self"
+ };
+ }
+
+ public ResourceLinks GetResourceLinks(ResourceType resourceType, IIdentifiable resource)
+ {
+ return new ResourceLinks
+ {
+ Self = "Resource:Self"
+ };
+ }
+
+ public RelationshipLinks GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable leftResource)
+ {
+ return new RelationshipLinks
+ {
+ Self = "Relationship:Self",
+ Related = "Relationship:Related"
+ };
+ }
+}
diff --git a/benchmarks/Tools/FakeRequestQueryStringAccessor.cs b/benchmarks/Tools/FakeRequestQueryStringAccessor.cs
new file mode 100644
index 0000000000..8b2b5540a1
--- /dev/null
+++ b/benchmarks/Tools/FakeRequestQueryStringAccessor.cs
@@ -0,0 +1,18 @@
+using JsonApiDotNetCore.QueryStrings;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.WebUtilities;
+
+namespace Benchmarks.Tools;
+
+///
+/// Enables to inject a query string, instead of obtaining it from .
+///
+internal sealed class FakeRequestQueryStringAccessor : IRequestQueryStringAccessor
+{
+ public IQueryCollection Query { get; private set; } = new QueryCollection();
+
+ public void SetQueryString(string queryString)
+ {
+ Query = new QueryCollection(QueryHelpers.ParseQuery(queryString));
+ }
+}
diff --git a/benchmarks/Tools/NeverResourceDefinitionAccessor.cs b/benchmarks/Tools/NeverResourceDefinitionAccessor.cs
new file mode 100644
index 0000000000..6e93519dae
--- /dev/null
+++ b/benchmarks/Tools/NeverResourceDefinitionAccessor.cs
@@ -0,0 +1,103 @@
+using System.Collections.Immutable;
+using JsonApiDotNetCore.Configuration;
+using JsonApiDotNetCore.Middleware;
+using JsonApiDotNetCore.Queries.Expressions;
+using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
+
+namespace Benchmarks.Tools;
+
+///
+/// Never calls into instances.
+///
+internal sealed class NeverResourceDefinitionAccessor : IResourceDefinitionAccessor
+{
+ public IImmutableSet OnApplyIncludes(ResourceType resourceType, IImmutableSet existingIncludes)
+ {
+ return existingIncludes;
+ }
+
+ public FilterExpression? OnApplyFilter(ResourceType resourceType, FilterExpression? existingFilter)
+ {
+ return existingFilter;
+ }
+
+ public SortExpression? OnApplySort(ResourceType resourceType, SortExpression? existingSort)
+ {
+ return existingSort;
+ }
+
+ public PaginationExpression? OnApplyPagination(ResourceType resourceType, PaginationExpression? existingPagination)
+ {
+ return existingPagination;
+ }
+
+ public SparseFieldSetExpression? OnApplySparseFieldSet(ResourceType resourceType, SparseFieldSetExpression? existingSparseFieldSet)
+ {
+ return existingSparseFieldSet;
+ }
+
+ public object? GetQueryableHandlerForQueryStringParameter(Type resourceClrType, string parameterName)
+ {
+ return null;
+ }
+
+ public IDictionary? GetMeta(ResourceType resourceType, IIdentifiable resourceInstance)
+ {
+ return null;
+ }
+
+ public Task OnPrepareWriteAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task OnSetToOneRelationshipAsync(TResource leftResource, HasOneAttribute hasOneRelationship,
+ IIdentifiable? rightResourceId, WriteOperationKind writeOperation, CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.FromResult(rightResourceId);
+ }
+
+ public Task OnSetToManyRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
+ WriteOperationKind writeOperation, CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task OnAddToRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
+ CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task OnRemoveFromRelationshipAsync(TResource leftResource, HasManyAttribute hasManyRelationship, ISet rightResourceIds,
+ CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task OnWritingAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task OnWriteSucceededAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken)
+ where TResource : class, IIdentifiable
+ {
+ return Task.CompletedTask;
+ }
+
+ public void OnDeserialize(IIdentifiable resource)
+ {
+ }
+
+ public void OnSerialize(IIdentifiable resource)
+ {
+ }
+}
diff --git a/benchmarks/Tools/NoMetaBuilder.cs b/benchmarks/Tools/NoMetaBuilder.cs
new file mode 100644
index 0000000000..db3ed7857e
--- /dev/null
+++ b/benchmarks/Tools/NoMetaBuilder.cs
@@ -0,0 +1,18 @@
+using JsonApiDotNetCore.Serialization.Response;
+
+namespace Benchmarks.Tools;
+
+///
+/// Doesn't produce any top-level meta.
+///
+internal sealed class NoMetaBuilder : IMetaBuilder
+{
+ public void Add(IDictionary values)
+ {
+ }
+
+ public IDictionary? Build()
+ {
+ return null;
+ }
+}
diff --git a/cleanupcode.ps1 b/cleanupcode.ps1
index 6db01a863a..bab8b82af1 100644
--- a/cleanupcode.ps1
+++ b/cleanupcode.ps1
@@ -1,17 +1,44 @@
#Requires -Version 7.0
-# This script reformats the entire codebase to make it compliant with our coding guidelines.
+# This script reformats (part of) the codebase to make it compliant with our coding guidelines.
-dotnet tool restore
+param(
+ # Git branch name or base commit hash to reformat only the subset of changed files. Omit for all files.
+ [string] $revision
+)
-if ($LASTEXITCODE -ne 0) {
- throw "Tool restore failed with exit code $LASTEXITCODE"
+function VerifySuccessExitCode {
+ if ($LastExitCode -ne 0) {
+ throw "Command failed with exit code $LastExitCode."
+ }
}
+dotnet tool restore
+VerifySuccessExitCode
+
dotnet restore
+VerifySuccessExitCode
-if ($LASTEXITCODE -ne 0) {
- throw "Package restore failed with exit code $LASTEXITCODE"
-}
+if ($revision) {
+ $headCommitHash = git rev-parse HEAD
+ VerifySuccessExitCode
-dotnet regitlint -s JsonApiDotNetCore.sln --print-command --disable-jb-path-hack --jb --profile='\"JADNC Full Cleanup\"' --jb --properties:Configuration=Release --jb --verbosity=WARN
+ $baseCommitHash = git rev-parse $revision
+ VerifySuccessExitCode
+
+ if ($baseCommitHash -eq $headCommitHash) {
+ Write-Output "Running code cleanup on staged/unstaged files."
+ dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified
+ VerifySuccessExitCode
+ }
+ else {
+ Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash, including staged/unstaged files."
+ dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash
+ VerifySuccessExitCode
+ }
+}
+else {
+ Write-Output "Running code cleanup on all files."
+ dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN
+ VerifySuccessExitCode
+}
diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/JsonApiDotNetCoreExampleClient.csproj b/src/Examples/JsonApiDotNetCoreExampleClient/JsonApiDotNetCoreExampleClient.csproj
index 17a8f6b436..fc2ebfd8c8 100644
--- a/src/Examples/JsonApiDotNetCoreExampleClient/JsonApiDotNetCoreExampleClient.csproj
+++ b/src/Examples/JsonApiDotNetCoreExampleClient/JsonApiDotNetCoreExampleClient.csproj
@@ -9,12 +9,12 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs
index a0e7159a89..e0ad6da30c 100644
--- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs
+++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs
@@ -3,6 +3,7 @@
using JsonApiDotNetCore.OpenApi.JsonApiObjects;
using JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships;
+using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -42,29 +43,30 @@ public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceG
_resourceObjectSchemaGenerator = new ResourceObjectSchemaGenerator(defaultSchemaGenerator, resourceGraph, options, _schemaRepositoryAccessor);
}
- public OpenApiSchema GenerateSchema(Type type, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null)
+ public OpenApiSchema GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null,
+ ApiParameterRouteInfo? routeInfo = null)
{
- ArgumentGuard.NotNull(type, nameof(type));
+ ArgumentGuard.NotNull(modelType, nameof(modelType));
ArgumentGuard.NotNull(schemaRepository, nameof(schemaRepository));
_schemaRepositoryAccessor.Current = schemaRepository;
- if (schemaRepository.TryLookupByType(type, out OpenApiSchema jsonApiDocumentSchema))
+ if (schemaRepository.TryLookupByType(modelType, out OpenApiSchema jsonApiDocumentSchema))
{
return jsonApiDocumentSchema;
}
- if (IsJsonApiDocument(type))
+ if (IsJsonApiDocument(modelType))
{
- OpenApiSchema schema = GenerateJsonApiDocumentSchema(type);
+ OpenApiSchema schema = GenerateJsonApiDocumentSchema(modelType);
- if (IsDataPropertyNullable(type))
+ if (IsDataPropertyNullable(modelType))
{
SetDataObjectSchemaToNullable(schema);
}
}
- return _defaultSchemaGenerator.GenerateSchema(type, schemaRepository, memberInfo, parameterInfo);
+ return _defaultSchemaGenerator.GenerateSchema(modelType, schemaRepository, memberInfo, parameterInfo);
}
private static bool IsJsonApiDocument(Type type)
diff --git a/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs b/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs
index 65800bba82..89a511b08e 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs
+++ b/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Linq;
using System.Text;
using Humanizer;
using Microsoft.CodeAnalysis;
@@ -11,166 +8,163 @@
#pragma warning disable RS2008 // Enable analyzer release tracking
-namespace JsonApiDotNetCore.SourceGenerators
+namespace JsonApiDotNetCore.SourceGenerators;
+// To debug in Visual Studio (requires v17.2 or higher):
+// - Set JsonApiDotNetCore.SourceGenerators as startup project
+// - Add a breakpoint at the start of the Initialize or Execute method
+// - Optional: change targetProject in Properties\launchSettings.json
+// - Press F5
+
+[Generator(LanguageNames.CSharp)]
+public sealed class ControllerSourceGenerator : ISourceGenerator
{
- // To debug in Visual Studio (requires v17.2 or higher):
- // - Set JsonApiDotNetCore.SourceGenerators as startup project
- // - Add a breakpoint at the start of the Initialize or Execute method
- // - Optional: change targetProject in Properties\launchSettings.json
- // - Press F5
-
- [Generator(LanguageNames.CSharp)]
- public sealed class ControllerSourceGenerator : ISourceGenerator
+ private const string Category = "JsonApiDotNetCore";
+
+ private static readonly DiagnosticDescriptor MissingInterfaceWarning = new("JADNC001", "Resource type does not implement IIdentifiable",
+ "Type '{0}' must implement IIdentifiable when using ResourceAttribute to auto-generate ASP.NET controllers", Category, DiagnosticSeverity.Warning,
+ true);
+
+ private static readonly DiagnosticDescriptor MissingIndentInTableError = new("JADNC900", "Internal error: Insufficient entries in IndentTable",
+ "Internal error: Missing entry in IndentTable for depth {0}", Category, DiagnosticSeverity.Warning, true);
+
+ // PERF: Heap-allocate the delegate only once, instead of per compilation.
+ private static readonly SyntaxReceiverCreator CreateSyntaxReceiver = static () => new TypeWithAttributeSyntaxReceiver();
+
+ public void Initialize(GeneratorInitializationContext context)
{
- private const string Category = "JsonApiDotNetCore";
+ context.RegisterForSyntaxNotifications(CreateSyntaxReceiver);
+ }
- private static readonly DiagnosticDescriptor MissingInterfaceWarning = new DiagnosticDescriptor("JADNC001",
- "Resource type does not implement IIdentifiable",
- "Type '{0}' must implement IIdentifiable when using ResourceAttribute to auto-generate ASP.NET controllers", Category,
- DiagnosticSeverity.Warning, true);
+ public void Execute(GeneratorExecutionContext context)
+ {
+ var receiver = (TypeWithAttributeSyntaxReceiver?)context.SyntaxReceiver;
- private static readonly DiagnosticDescriptor MissingIndentInTableError = new DiagnosticDescriptor("JADNC900",
- "Internal error: Insufficient entries in IndentTable", "Internal error: Missing entry in IndentTable for depth {0}", Category,
- DiagnosticSeverity.Warning, true);
+ if (receiver == null)
+ {
+ return;
+ }
- // PERF: Heap-allocate the delegate only once, instead of per compilation.
- private static readonly SyntaxReceiverCreator CreateSyntaxReceiver = () => new TypeWithAttributeSyntaxReceiver();
+ INamedTypeSymbol? resourceAttributeType = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.Annotations.ResourceAttribute");
+ INamedTypeSymbol? identifiableOpenInterface = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.IIdentifiable`1");
+ INamedTypeSymbol? loggerFactoryInterface = context.Compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILoggerFactory");
- public void Initialize(GeneratorInitializationContext context)
+ if (resourceAttributeType == null || identifiableOpenInterface == null || loggerFactoryInterface == null)
{
- context.RegisterForSyntaxNotifications(CreateSyntaxReceiver);
+ return;
}
- public void Execute(GeneratorExecutionContext context)
+ var controllerNamesInUse = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var writer = new SourceCodeWriter(context, MissingIndentInTableError);
+
+ foreach (TypeDeclarationSyntax? typeDeclarationSyntax in receiver.TypeDeclarations)
{
- var receiver = (TypeWithAttributeSyntaxReceiver)context.SyntaxReceiver;
+ // PERF: Note that our code runs on every keystroke in the IDE, which makes it critical to provide near-realtime performance.
+ // This means keeping an eye on memory allocations and bailing out early when compilations are cancelled while the user is still typing.
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ SemanticModel semanticModel = context.Compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree);
+ INamedTypeSymbol? resourceType = semanticModel.GetDeclaredSymbol(typeDeclarationSyntax, context.CancellationToken);
- if (receiver == null)
+ if (resourceType == null)
{
- return;
+ continue;
}
- INamedTypeSymbol resourceAttributeType = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.Annotations.ResourceAttribute");
- INamedTypeSymbol identifiableOpenInterface = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.IIdentifiable`1");
- INamedTypeSymbol loggerFactoryInterface = context.Compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILoggerFactory");
+ AttributeData? resourceAttributeData = FirstOrDefault(resourceType.GetAttributes(), resourceAttributeType,
+ static (data, type) => SymbolEqualityComparer.Default.Equals(data.AttributeClass, type));
- if (resourceAttributeType == null || identifiableOpenInterface == null || loggerFactoryInterface == null)
+ if (resourceAttributeData == null)
{
- return;
+ continue;
}
- var controllerNamesInUse = new Dictionary(StringComparer.OrdinalIgnoreCase);
- var writer = new SourceCodeWriter(context, MissingIndentInTableError);
+ TypedConstant endpointsArgument =
+ resourceAttributeData.NamedArguments.FirstOrDefault(static pair => pair.Key == "GenerateControllerEndpoints").Value;
- foreach (TypeDeclarationSyntax typeDeclarationSyntax in receiver.TypeDeclarations)
+ if (endpointsArgument.Value != null && (JsonApiEndpointsCopy)endpointsArgument.Value == JsonApiEndpointsCopy.None)
{
- // PERF: Note that our code runs on every keystroke in the IDE, which makes it critical to provide near-realtime performance.
- // This means keeping an eye on memory allocations and bailing out early when compilations are cancelled while the user is still typing.
- context.CancellationToken.ThrowIfCancellationRequested();
-
- SemanticModel semanticModel = context.Compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree);
- INamedTypeSymbol resourceType = semanticModel.GetDeclaredSymbol(typeDeclarationSyntax, context.CancellationToken);
-
- if (resourceType == null)
- {
- continue;
- }
-
- AttributeData resourceAttributeData = FirstOrDefault(resourceType.GetAttributes(), resourceAttributeType,
- (data, type) => SymbolEqualityComparer.Default.Equals(data.AttributeClass, type));
-
- if (resourceAttributeData == null)
- {
- continue;
- }
-
- TypedConstant endpointsArgument = resourceAttributeData.NamedArguments.FirstOrDefault(pair => pair.Key == "GenerateControllerEndpoints").Value;
-
- if (endpointsArgument.Value != null && (JsonApiEndpointsCopy)endpointsArgument.Value == JsonApiEndpointsCopy.None)
- {
- continue;
- }
-
- TypedConstant controllerNamespaceArgument =
- resourceAttributeData.NamedArguments.FirstOrDefault(pair => pair.Key == "ControllerNamespace").Value;
-
- string controllerNamespace = GetControllerNamespace(controllerNamespaceArgument, resourceType);
-
- INamedTypeSymbol identifiableClosedInterface = FirstOrDefault(resourceType.AllInterfaces, identifiableOpenInterface,
- (@interface, openInterface) => @interface.IsGenericType &&
- SymbolEqualityComparer.Default.Equals(@interface.ConstructedFrom, openInterface));
+ continue;
+ }
- if (identifiableClosedInterface == null)
- {
- var diagnostic = Diagnostic.Create(MissingInterfaceWarning, typeDeclarationSyntax.GetLocation(), resourceType.Name);
- context.ReportDiagnostic(diagnostic);
- continue;
- }
+ TypedConstant controllerNamespaceArgument =
+ resourceAttributeData.NamedArguments.FirstOrDefault(static pair => pair.Key == "ControllerNamespace").Value;
- ITypeSymbol idType = identifiableClosedInterface.TypeArguments[0];
- string controllerName = $"{resourceType.Name.Pluralize()}Controller";
- JsonApiEndpointsCopy endpointsToGenerate = (JsonApiEndpointsCopy?)(int?)endpointsArgument.Value ?? JsonApiEndpointsCopy.All;
+ string? controllerNamespace = GetControllerNamespace(controllerNamespaceArgument, resourceType);
- string sourceCode = writer.Write(resourceType, idType, endpointsToGenerate, controllerNamespace, controllerName, loggerFactoryInterface);
- SourceText sourceText = SourceText.From(sourceCode, Encoding.UTF8);
+ INamedTypeSymbol? identifiableClosedInterface = FirstOrDefault(resourceType.AllInterfaces, identifiableOpenInterface,
+ static (@interface, openInterface) =>
+ @interface.IsGenericType && SymbolEqualityComparer.Default.Equals(@interface.ConstructedFrom, openInterface));
- string fileName = GetUniqueFileName(controllerName, controllerNamesInUse);
- context.AddSource(fileName, sourceText);
+ if (identifiableClosedInterface == null)
+ {
+ var diagnostic = Diagnostic.Create(MissingInterfaceWarning, typeDeclarationSyntax.GetLocation(), resourceType.Name);
+ context.ReportDiagnostic(diagnostic);
+ continue;
}
- }
- private static TElement FirstOrDefault(ImmutableArray source, TContext context, Func predicate)
- {
- // PERF: Using this method enables to avoid allocating a closure in the passed lambda expression.
- // See https://www.jetbrains.com/help/resharper/2021.2/Fixing_Issues_Found_by_DPA.html#closures-in-lambda-expressions.
+ ITypeSymbol idType = identifiableClosedInterface.TypeArguments[0];
+ string controllerName = $"{resourceType.Name.Pluralize()}Controller";
+ JsonApiEndpointsCopy endpointsToGenerate = (JsonApiEndpointsCopy?)(int?)endpointsArgument.Value ?? JsonApiEndpointsCopy.All;
- foreach (TElement element in source)
- {
- if (predicate(element, context))
- {
- return element;
- }
- }
+ string sourceCode = writer.Write(resourceType, idType, endpointsToGenerate, controllerNamespace, controllerName, loggerFactoryInterface);
+ SourceText sourceText = SourceText.From(sourceCode, Encoding.UTF8);
- return default;
+ string fileName = GetUniqueFileName(controllerName, controllerNamesInUse);
+ context.AddSource(fileName, sourceText);
}
+ }
- private static string GetControllerNamespace(TypedConstant controllerNamespaceArgument, INamedTypeSymbol resourceType)
+ private static TElement? FirstOrDefault(ImmutableArray source, TContext context, Func predicate)
+ {
+ // PERF: Using this method enables to avoid allocating a closure in the passed lambda expression.
+ // See https://www.jetbrains.com/help/resharper/2021.2/Fixing_Issues_Found_by_DPA.html#closures-in-lambda-expressions.
+
+ foreach (TElement element in source)
{
- if (!controllerNamespaceArgument.IsNull)
+ if (predicate(element, context))
{
- return (string)controllerNamespaceArgument.Value;
+ return element;
}
+ }
- if (resourceType.ContainingNamespace.IsGlobalNamespace)
- {
- return null;
- }
+ return default;
+ }
- if (resourceType.ContainingNamespace.ContainingNamespace.IsGlobalNamespace)
- {
- return "Controllers";
- }
+ private static string? GetControllerNamespace(TypedConstant controllerNamespaceArgument, INamedTypeSymbol resourceType)
+ {
+ if (!controllerNamespaceArgument.IsNull)
+ {
+ return (string?)controllerNamespaceArgument.Value;
+ }
- return $"{resourceType.ContainingNamespace.ContainingNamespace}.Controllers";
+ if (resourceType.ContainingNamespace.IsGlobalNamespace)
+ {
+ return null;
}
- private static string GetUniqueFileName(string controllerName, IDictionary controllerNamesInUse)
+ if (resourceType.ContainingNamespace.ContainingNamespace.IsGlobalNamespace)
{
- // We emit unique file names to prevent a failure in the source generator, but also because our test suite
- // may contain two resources with the same class name in different namespaces. That works, as long as only
- // one of its controllers gets registered.
+ return "Controllers";
+ }
- if (controllerNamesInUse.TryGetValue(controllerName, out int lastIndex))
- {
- lastIndex++;
- controllerNamesInUse[controllerName] = lastIndex;
+ return $"{resourceType.ContainingNamespace.ContainingNamespace}.Controllers";
+ }
- return $"{controllerName}{lastIndex}.g.cs";
- }
+ private static string GetUniqueFileName(string controllerName, IDictionary controllerNamesInUse)
+ {
+ // We emit unique file names to prevent a failure in the source generator, but also because our test suite
+ // may contain two resources with the same class name in different namespaces. That works, as long as only
+ // one of its controllers gets registered.
- controllerNamesInUse[controllerName] = 1;
- return $"{controllerName}.g.cs";
+ if (controllerNamesInUse.TryGetValue(controllerName, out int lastIndex))
+ {
+ lastIndex++;
+ controllerNamesInUse[controllerName] = lastIndex;
+
+ return $"{controllerName}{lastIndex}.g.cs";
}
+
+ controllerNamesInUse[controllerName] = 1;
+ return $"{controllerName}.g.cs";
}
}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
index bcd8c06b0a..8bf3e90cf6 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
+++ b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
@@ -5,8 +5,7 @@
true
false
$(NoWarn);NU5128
- disable
- disable
+ latest
true
diff --git a/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs b/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs
index 14134adcfd..911be3f359 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs
+++ b/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs
@@ -1,26 +1,23 @@
-using System;
+namespace JsonApiDotNetCore.SourceGenerators;
-namespace JsonApiDotNetCore.SourceGenerators
+// IMPORTANT: A copy of this type exists in the JsonApiDotNetCore project. Keep these in sync when making changes.
+[Flags]
+public enum JsonApiEndpointsCopy
{
- // IMPORTANT: A copy of this type exists in the JsonApiDotNetCore project. Keep these in sync when making changes.
- [Flags]
- public enum JsonApiEndpointsCopy
- {
- None = 0,
- GetCollection = 1,
- GetSingle = 1 << 1,
- GetSecondary = 1 << 2,
- GetRelationship = 1 << 3,
- Post = 1 << 4,
- PostRelationship = 1 << 5,
- Patch = 1 << 6,
- PatchRelationship = 1 << 7,
- Delete = 1 << 8,
- DeleteRelationship = 1 << 9,
+ None = 0,
+ GetCollection = 1,
+ GetSingle = 1 << 1,
+ GetSecondary = 1 << 2,
+ GetRelationship = 1 << 3,
+ Post = 1 << 4,
+ PostRelationship = 1 << 5,
+ Patch = 1 << 6,
+ PatchRelationship = 1 << 7,
+ Delete = 1 << 8,
+ DeleteRelationship = 1 << 9,
- Query = GetCollection | GetSingle | GetSecondary | GetRelationship,
- Command = Post | PostRelationship | Patch | PatchRelationship | Delete | DeleteRelationship,
+ Query = GetCollection | GetSingle | GetSecondary | GetRelationship,
+ Command = Post | PostRelationship | Patch | PatchRelationship | Delete | DeleteRelationship,
- All = Query | Command
- }
+ All = Query | Command
}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs b/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs
index e03e3cbad2..13dc91a836 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs
+++ b/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs
@@ -1,266 +1,271 @@
-using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;
-namespace JsonApiDotNetCore.SourceGenerators
+namespace JsonApiDotNetCore.SourceGenerators;
+
+///
+/// Writes the source code for an ASP.NET controller for a JSON:API resource.
+///
+internal sealed class SourceCodeWriter
{
- ///
- /// Writes the source code for an ASP.NET controller for a JSON:API resource.
- ///
- internal sealed class SourceCodeWriter
- {
- private const int SpacesPerIndent = 4;
+ private const int SpacesPerIndent = 4;
- private static readonly IDictionary IndentTable = new Dictionary
+ private static readonly IDictionary IndentTable = new Dictionary
+ {
+ [0] = string.Empty,
+ [1] = new(' ', 1 * SpacesPerIndent),
+ [2] = new(' ', 2 * SpacesPerIndent),
+ [3] = new(' ', 3 * SpacesPerIndent)
+ };
+
+ private static readonly IDictionary AggregateEndpointToServiceNameMap =
+ new Dictionary
{
- [0] = string.Empty,
- [1] = new string(' ', 1 * SpacesPerIndent),
- [2] = new string(' ', 2 * SpacesPerIndent),
- [3] = new string(' ', 3 * SpacesPerIndent)
+ [JsonApiEndpointsCopy.All] = ("IResourceService", "resourceService"),
+ [JsonApiEndpointsCopy.Query] = ("IResourceQueryService", "queryService"),
+ [JsonApiEndpointsCopy.Command] = ("IResourceCommandService", "commandService")
};
- private static readonly IDictionary AggregateEndpointToServiceNameMap =
- new Dictionary
- {
- [JsonApiEndpointsCopy.All] = ("IResourceService", "resourceService"),
- [JsonApiEndpointsCopy.Query] = ("IResourceQueryService", "queryService"),
- [JsonApiEndpointsCopy.Command] = ("IResourceCommandService", "commandService")
- };
-
- private static readonly IDictionary EndpointToServiceNameMap =
- new Dictionary
- {
- [JsonApiEndpointsCopy.GetCollection] = ("IGetAllService", "getAll"),
- [JsonApiEndpointsCopy.GetSingle] = ("IGetByIdService", "getById"),
- [JsonApiEndpointsCopy.GetSecondary] = ("IGetSecondaryService", "getSecondary"),
- [JsonApiEndpointsCopy.GetRelationship] = ("IGetRelationshipService", "getRelationship"),
- [JsonApiEndpointsCopy.Post] = ("ICreateService", "create"),
- [JsonApiEndpointsCopy.PostRelationship] = ("IAddToRelationshipService", "addToRelationship"),
- [JsonApiEndpointsCopy.Patch] = ("IUpdateService", "update"),
- [JsonApiEndpointsCopy.PatchRelationship] = ("ISetRelationshipService", "setRelationship"),
- [JsonApiEndpointsCopy.Delete] = ("IDeleteService", "delete"),
- [JsonApiEndpointsCopy.DeleteRelationship] = ("IRemoveFromRelationshipService", "removeFromRelationship")
- };
-
- private readonly GeneratorExecutionContext _context;
- private readonly DiagnosticDescriptor _missingIndentInTableErrorDescriptor;
-
- private readonly StringBuilder _sourceBuilder = new StringBuilder();
- private int _depth;
-
- public SourceCodeWriter(GeneratorExecutionContext context, DiagnosticDescriptor missingIndentInTableErrorDescriptor)
+ private static readonly IDictionary EndpointToServiceNameMap =
+ new Dictionary
{
- _context = context;
- _missingIndentInTableErrorDescriptor = missingIndentInTableErrorDescriptor;
- }
-
- public string Write(INamedTypeSymbol resourceType, ITypeSymbol idType, JsonApiEndpointsCopy endpointsToGenerate, string controllerNamespace,
- string controllerName, INamedTypeSymbol loggerFactoryInterface)
- {
- _sourceBuilder.Clear();
- _depth = 0;
-
- if (idType.IsReferenceType && idType.NullableAnnotation == NullableAnnotation.Annotated)
- {
- WriteNullableEnable();
- }
+ [JsonApiEndpointsCopy.GetCollection] = ("IGetAllService", "getAll"),
+ [JsonApiEndpointsCopy.GetSingle] = ("IGetByIdService", "getById"),
+ [JsonApiEndpointsCopy.GetSecondary] = ("IGetSecondaryService", "getSecondary"),
+ [JsonApiEndpointsCopy.GetRelationship] = ("IGetRelationshipService", "getRelationship"),
+ [JsonApiEndpointsCopy.Post] = ("ICreateService", "create"),
+ [JsonApiEndpointsCopy.PostRelationship] = ("IAddToRelationshipService", "addToRelationship"),
+ [JsonApiEndpointsCopy.Patch] = ("IUpdateService", "update"),
+ [JsonApiEndpointsCopy.PatchRelationship] = ("ISetRelationshipService", "setRelationship"),
+ [JsonApiEndpointsCopy.Delete] = ("IDeleteService", "delete"),
+ [JsonApiEndpointsCopy.DeleteRelationship] = ("IRemoveFromRelationshipService", "removeFromRelationship")
+ };
- WriteNamespaceImports(loggerFactoryInterface, resourceType);
+ private readonly GeneratorExecutionContext _context;
+ private readonly DiagnosticDescriptor _missingIndentInTableErrorDescriptor;
- if (controllerNamespace != null)
- {
- WriteNamespaceDeclaration(controllerNamespace);
- }
+ private readonly StringBuilder _sourceBuilder = new();
+ private int _depth;
- WriteOpenClassDeclaration(controllerName, endpointsToGenerate, resourceType, idType);
- _depth++;
+ public SourceCodeWriter(GeneratorExecutionContext context, DiagnosticDescriptor missingIndentInTableErrorDescriptor)
+ {
+ _context = context;
+ _missingIndentInTableErrorDescriptor = missingIndentInTableErrorDescriptor;
+ }
- WriteConstructor(controllerName, loggerFactoryInterface, endpointsToGenerate, resourceType, idType);
+ public string Write(INamedTypeSymbol resourceType, ITypeSymbol idType, JsonApiEndpointsCopy endpointsToGenerate, string? controllerNamespace,
+ string controllerName, INamedTypeSymbol loggerFactoryInterface)
+ {
+ _sourceBuilder.Clear();
+ _depth = 0;
- _depth--;
- WriteCloseCurly();
+ WriteAutoGeneratedComment();
- return _sourceBuilder.ToString();
+ if (idType.IsReferenceType && idType.NullableAnnotation == NullableAnnotation.Annotated)
+ {
+ WriteNullableEnable();
}
- private void WriteNullableEnable()
+ WriteNamespaceImports(loggerFactoryInterface, resourceType);
+
+ if (controllerNamespace != null)
{
- _sourceBuilder.AppendLine("#nullable enable");
- _sourceBuilder.AppendLine();
+ WriteNamespaceDeclaration(controllerNamespace);
}
- private void WriteNamespaceImports(INamedTypeSymbol loggerFactoryInterface, INamedTypeSymbol resourceType)
- {
- _sourceBuilder.AppendLine($@"using {loggerFactoryInterface.ContainingNamespace};");
+ WriteOpenClassDeclaration(controllerName, endpointsToGenerate, resourceType, idType);
+ _depth++;
- _sourceBuilder.AppendLine("using JsonApiDotNetCore.Configuration;");
- _sourceBuilder.AppendLine("using JsonApiDotNetCore.Controllers;");
- _sourceBuilder.AppendLine("using JsonApiDotNetCore.Services;");
+ WriteConstructor(controllerName, loggerFactoryInterface, endpointsToGenerate, resourceType, idType);
- if (!resourceType.ContainingNamespace.IsGlobalNamespace)
- {
- _sourceBuilder.AppendLine($"using {resourceType.ContainingNamespace};");
- }
+ _depth--;
+ WriteCloseCurly();
- _sourceBuilder.AppendLine();
- }
+ return _sourceBuilder.ToString();
+ }
- private void WriteNamespaceDeclaration(string controllerNamespace)
- {
- _sourceBuilder.AppendLine($"namespace {controllerNamespace};");
- _sourceBuilder.AppendLine();
- }
+ private void WriteAutoGeneratedComment()
+ {
+ _sourceBuilder.AppendLine("// ");
+ _sourceBuilder.AppendLine();
+ }
- private void WriteOpenClassDeclaration(string controllerName, JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType,
- ITypeSymbol idType)
- {
- string baseClassName = GetControllerBaseClassName(endpointsToGenerate);
+ private void WriteNullableEnable()
+ {
+ _sourceBuilder.AppendLine("#nullable enable");
+ _sourceBuilder.AppendLine();
+ }
- WriteIndent();
- _sourceBuilder.AppendLine($@"public sealed partial class {controllerName} : {baseClassName}<{resourceType.Name}, {idType}>");
+ private void WriteNamespaceImports(INamedTypeSymbol loggerFactoryInterface, INamedTypeSymbol resourceType)
+ {
+ _sourceBuilder.AppendLine($@"using {loggerFactoryInterface.ContainingNamespace};");
- WriteOpenCurly();
- }
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Configuration;");
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Controllers;");
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Services;");
- private static string GetControllerBaseClassName(JsonApiEndpointsCopy endpointsToGenerate)
+ if (!resourceType.ContainingNamespace.IsGlobalNamespace)
{
- switch (endpointsToGenerate)
- {
- case JsonApiEndpointsCopy.Query:
- {
- return "JsonApiQueryController";
- }
- case JsonApiEndpointsCopy.Command:
- {
- return "JsonApiCommandController";
- }
- default:
- {
- return "JsonApiController";
- }
- }
+ _sourceBuilder.AppendLine($"using {resourceType.ContainingNamespace};");
}
- private void WriteConstructor(string controllerName, INamedTypeSymbol loggerFactoryInterface, JsonApiEndpointsCopy endpointsToGenerate,
- INamedTypeSymbol resourceType, ITypeSymbol idType)
- {
- string loggerName = loggerFactoryInterface.Name;
+ _sourceBuilder.AppendLine();
+ }
+
+ private void WriteNamespaceDeclaration(string controllerNamespace)
+ {
+ _sourceBuilder.AppendLine($"namespace {controllerNamespace};");
+ _sourceBuilder.AppendLine();
+ }
+
+ private void WriteOpenClassDeclaration(string controllerName, JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ string baseClassName = GetControllerBaseClassName(endpointsToGenerate);
- WriteIndent();
- _sourceBuilder.AppendLine($"public {controllerName}(IJsonApiOptions options, IResourceGraph resourceGraph, {loggerName} loggerFactory,");
+ WriteIndent();
+ _sourceBuilder.AppendLine($@"public sealed partial class {controllerName} : {baseClassName}<{resourceType.Name}, {idType}>");
- _depth++;
+ WriteOpenCurly();
+ }
- if (AggregateEndpointToServiceNameMap.TryGetValue(endpointsToGenerate, out (string ServiceName, string ParameterName) value))
+ private static string GetControllerBaseClassName(JsonApiEndpointsCopy endpointsToGenerate)
+ {
+ switch (endpointsToGenerate)
+ {
+ case JsonApiEndpointsCopy.Query:
+ {
+ return "JsonApiQueryController";
+ }
+ case JsonApiEndpointsCopy.Command:
{
- WriteParameterListForShortConstructor(value.ServiceName, value.ParameterName, resourceType, idType);
+ return "JsonApiCommandController";
}
- else
+ default:
{
- WriteParameterListForLongConstructor(endpointsToGenerate, resourceType, idType);
+ return "JsonApiController";
}
+ }
+ }
- _depth--;
+ private void WriteConstructor(string controllerName, INamedTypeSymbol loggerFactoryInterface, JsonApiEndpointsCopy endpointsToGenerate,
+ INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ string loggerName = loggerFactoryInterface.Name;
- WriteOpenCurly();
- WriteCloseCurly();
- }
+ WriteIndent();
+ _sourceBuilder.AppendLine($"public {controllerName}(IJsonApiOptions options, IResourceGraph resourceGraph, {loggerName} loggerFactory,");
- private void WriteParameterListForShortConstructor(string serviceName, string parameterName, INamedTypeSymbol resourceType, ITypeSymbol idType)
- {
- WriteIndent();
- _sourceBuilder.AppendLine($"{serviceName}<{resourceType.Name}, {idType}> {parameterName})");
+ _depth++;
- WriteIndent();
- _sourceBuilder.AppendLine($": base(options, resourceGraph, loggerFactory, {parameterName})");
+ if (AggregateEndpointToServiceNameMap.TryGetValue(endpointsToGenerate, out (string ServiceName, string ParameterName) value))
+ {
+ WriteParameterListForShortConstructor(value.ServiceName, value.ParameterName, resourceType, idType);
}
-
- private void WriteParameterListForLongConstructor(JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ else
{
- bool isFirstEntry = true;
+ WriteParameterListForLongConstructor(endpointsToGenerate, resourceType, idType);
+ }
+
+ _depth--;
+
+ WriteOpenCurly();
+ WriteCloseCurly();
+ }
+
+ private void WriteParameterListForShortConstructor(string serviceName, string parameterName, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine($"{serviceName}<{resourceType.Name}, {idType}> {parameterName})");
+
+ WriteIndent();
+ _sourceBuilder.AppendLine($": base(options, resourceGraph, loggerFactory, {parameterName})");
+ }
+
+ private void WriteParameterListForLongConstructor(JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ bool isFirstEntry = true;
- foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ {
+ if ((endpointsToGenerate & entry.Key) == entry.Key)
{
- if ((endpointsToGenerate & entry.Key) == entry.Key)
+ if (isFirstEntry)
+ {
+ isFirstEntry = false;
+ }
+ else
{
- if (isFirstEntry)
- {
- isFirstEntry = false;
- }
- else
- {
- _sourceBuilder.AppendLine(Tokens.Comma);
- }
-
- WriteIndent();
- _sourceBuilder.Append($"{entry.Value.ServiceName}<{resourceType.Name}, {idType}> {entry.Value.ParameterName}");
+ _sourceBuilder.AppendLine(Tokens.Comma);
}
+
+ WriteIndent();
+ _sourceBuilder.Append($"{entry.Value.ServiceName}<{resourceType.Name}, {idType}> {entry.Value.ParameterName}");
}
+ }
- _sourceBuilder.AppendLine(Tokens.CloseParen);
+ _sourceBuilder.AppendLine(Tokens.CloseParen);
- WriteIndent();
- _sourceBuilder.AppendLine(": base(options, resourceGraph, loggerFactory,");
+ WriteIndent();
+ _sourceBuilder.AppendLine(": base(options, resourceGraph, loggerFactory,");
- isFirstEntry = true;
- _depth++;
+ isFirstEntry = true;
+ _depth++;
- foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ {
+ if ((endpointsToGenerate & entry.Key) == entry.Key)
{
- if ((endpointsToGenerate & entry.Key) == entry.Key)
+ if (isFirstEntry)
{
- if (isFirstEntry)
- {
- isFirstEntry = false;
- }
- else
- {
- _sourceBuilder.AppendLine(Tokens.Comma);
- }
-
- WriteIndent();
- _sourceBuilder.Append($"{entry.Value.ParameterName}: {entry.Value.ParameterName}");
+ isFirstEntry = false;
+ }
+ else
+ {
+ _sourceBuilder.AppendLine(Tokens.Comma);
}
- }
- _sourceBuilder.AppendLine(Tokens.CloseParen);
- _depth--;
+ WriteIndent();
+ _sourceBuilder.Append($"{entry.Value.ParameterName}: {entry.Value.ParameterName}");
+ }
}
- private void WriteOpenCurly()
- {
- WriteIndent();
- _sourceBuilder.AppendLine(Tokens.OpenCurly);
- }
+ _sourceBuilder.AppendLine(Tokens.CloseParen);
+ _depth--;
+ }
- private void WriteCloseCurly()
- {
- WriteIndent();
- _sourceBuilder.AppendLine(Tokens.CloseCurly);
- }
+ private void WriteOpenCurly()
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine(Tokens.OpenCurly);
+ }
- private void WriteIndent()
- {
- // PERF: Reuse pre-calculated indents instead of allocating a new string each time.
- if (!IndentTable.TryGetValue(_depth, out string indent))
- {
- var diagnostic = Diagnostic.Create(_missingIndentInTableErrorDescriptor, Location.None, _depth.ToString());
- _context.ReportDiagnostic(diagnostic);
+ private void WriteCloseCurly()
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine(Tokens.CloseCurly);
+ }
- indent = new string(' ', _depth * SpacesPerIndent);
- }
+ private void WriteIndent()
+ {
+ // PERF: Reuse pre-calculated indents instead of allocating a new string each time.
+ if (!IndentTable.TryGetValue(_depth, out string? indent))
+ {
+ var diagnostic = Diagnostic.Create(_missingIndentInTableErrorDescriptor, Location.None, _depth.ToString());
+ _context.ReportDiagnostic(diagnostic);
- _sourceBuilder.Append(indent);
+ indent = new string(' ', _depth * SpacesPerIndent);
}
+ _sourceBuilder.Append(indent);
+ }
+
#pragma warning disable AV1008 // Class should not be static
- private static class Tokens
- {
- public const string OpenCurly = "{";
- public const string CloseCurly = "}";
- public const string CloseParen = ")";
- public const string Comma = ",";
- }
-#pragma warning restore AV1008 // Class should not be static
+ private static class Tokens
+ {
+ public const string OpenCurly = "{";
+ public const string CloseCurly = "}";
+ public const string CloseParen = ")";
+ public const string Comma = ",";
}
+#pragma warning restore AV1008 // Class should not be static
}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs
index 0fbc18a758..b23de19cc9 100644
--- a/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs
+++ b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs
@@ -1,41 +1,39 @@
-using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace JsonApiDotNetCore.SourceGenerators
+namespace JsonApiDotNetCore.SourceGenerators;
+
+///
+/// Collects type declarations in the project that have at least one attribute on them. Because this receiver operates at the syntax level, we cannot
+/// check for the expected attribute. This must be done during semantic analysis, because source code may contain any of these:
+/// { }
+///
+/// [ResourceAttribute]
+/// public class ExampleResource2 : Identifiable { }
+///
+/// [AlternateNamespaceName.Annotations.Resource]
+/// public class ExampleResource3 : Identifiable { }
+///
+/// [AlternateTypeName]
+/// public class ExampleResource4 : Identifiable { }
+/// ]]>
+///
+///
+internal sealed class TypeWithAttributeSyntaxReceiver : ISyntaxReceiver
{
- ///
- /// Collects type declarations in the project that have at least one attribute on them. Because this receiver operates at the syntax level, we cannot
- /// check for the expected attribute. This must be done during semantic analysis, because source code may contain any of these:
- /// { }
- ///
- /// [ResourceAttribute]
- /// public class ExampleResource2 : Identifiable { }
- ///
- /// [AlternateNamespaceName.Annotations.Resource]
- /// public class ExampleResource3 : Identifiable { }
- ///
- /// [AlternateTypeName]
- /// public class ExampleResource4 : Identifiable { }
- /// ]]>
- ///
- ///
- internal sealed class TypeWithAttributeSyntaxReceiver : ISyntaxReceiver
- {
- public readonly ISet TypeDeclarations = new HashSet();
+ public readonly ISet TypeDeclarations = new HashSet();
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax && typeDeclarationSyntax.AttributeLists.Any())
{
- if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax && typeDeclarationSyntax.AttributeLists.Any())
- {
- TypeDeclarations.Add(typeDeclarationSyntax);
- }
+ TypeDeclarations.Add(typeDeclarationSyntax);
}
}
}
diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
index eda6374acf..46603260cd 100644
--- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
@@ -11,8 +11,8 @@ namespace JsonApiDotNetCore.Configuration;
[PublicAPI]
public sealed class JsonApiOptions : IJsonApiOptions
{
- private Lazy _lazySerializerWriteOptions;
- private Lazy _lazySerializerReadOptions;
+ private readonly Lazy _lazySerializerWriteOptions;
+ private readonly Lazy _lazySerializerReadOptions;
///
JsonSerializerOptions IJsonApiOptions.SerializerReadOptions => _lazySerializerReadOptions.Value;
@@ -102,15 +102,10 @@ public sealed class JsonApiOptions : IJsonApiOptions
}
};
- static JsonApiOptions()
- {
- // Bug workaround for https://github.com/dotnet/efcore/issues/27436
- AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue26779", true);
- }
-
public JsonApiOptions()
{
- _lazySerializerReadOptions = new Lazy(() => new JsonSerializerOptions(SerializerOptions), LazyThreadSafetyMode.PublicationOnly);
+ _lazySerializerReadOptions =
+ new Lazy(() => new JsonSerializerOptions(SerializerOptions), LazyThreadSafetyMode.ExecutionAndPublication);
_lazySerializerWriteOptions = new Lazy(() => new JsonSerializerOptions(SerializerOptions)
{
@@ -119,6 +114,6 @@ public JsonApiOptions()
new WriteOnlyDocumentConverter(),
new WriteOnlyRelationshipObjectConverter()
}
- }, LazyThreadSafetyMode.PublicationOnly);
+ }, LazyThreadSafetyMode.ExecutionAndPublication);
}
}
diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs b/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs
index 94507750da..2e15e6ae9a 100644
--- a/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs
+++ b/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs
@@ -72,7 +72,7 @@ public async Task InvokeAsync(HttpContext httpContext, IControllerResourceMappin
return;
}
- SetupOperationsRequest((JsonApiRequest)request, options, httpContext.Request);
+ SetupOperationsRequest((JsonApiRequest)request);
httpContext.RegisterJsonApiRequest();
}
@@ -280,7 +280,7 @@ private static bool IsRouteForOperations(RouteValueDictionary routeValues)
return actionName == "PostOperations";
}
- private static void SetupOperationsRequest(JsonApiRequest request, IJsonApiOptions options, HttpRequest httpRequest)
+ private static void SetupOperationsRequest(JsonApiRequest request)
{
request.IsReadOnly = false;
request.Kind = EndpointKind.AtomicOperations;
diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs
index a453921989..14d2f1ec15 100644
--- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs
+++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs
@@ -202,7 +202,7 @@ public string Path
var pathBuilder = new StringBuilder();
IncludeTreeNode? parent = this;
- while (parent is { Relationship: not HiddenRootRelationship })
+ while (parent is { Relationship: not HiddenRootRelationshipAttribute })
{
pathBuilder.Insert(0, pathBuilder.Length > 0 ? $"{parent.Relationship.PublicName}." : parent.Relationship.PublicName);
parent = parent._parent;
@@ -220,7 +220,7 @@ private IncludeTreeNode(RelationshipAttribute relationship, IncludeTreeNode? par
public static IncludeTreeNode CreateRoot(ResourceType resourceType)
{
- var relationship = new HiddenRootRelationship(resourceType);
+ var relationship = new HiddenRootRelationshipAttribute(resourceType);
return new IncludeTreeNode(relationship, null);
}
@@ -242,7 +242,7 @@ public IncludeExpression ToExpression()
{
IncludeElementExpression element = ToElementExpression();
- if (element.Relationship is HiddenRootRelationship)
+ if (element.Relationship is HiddenRootRelationshipAttribute)
{
return new IncludeExpression(element.Children);
}
@@ -262,9 +262,9 @@ public override string ToString()
return include.ToFullString();
}
- private sealed class HiddenRootRelationship : RelationshipAttribute
+ private sealed class HiddenRootRelationshipAttribute : RelationshipAttribute
{
- public HiddenRootRelationship(ResourceType rightType)
+ public HiddenRootRelationshipAttribute(ResourceType rightType)
{
ArgumentGuard.NotNull(rightType, nameof(rightType));
diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs
index 358f63a2cd..1b807fd24f 100644
--- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs
+++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs
@@ -547,6 +547,34 @@ private void MarkRelationshipAsLoaded(TResource leftResource, RelationshipAttrib
EntityEntry leftEntry = _dbContext.Entry(leftResource);
CollectionEntry rightCollectionEntry = leftEntry.Collection(relationship.Property.Name);
rightCollectionEntry.IsLoaded = true;
+
+ if (rightCollectionEntry.Metadata is ISkipNavigation skipNavigation)
+ {
+ MarkManyToManyRelationshipAsLoaded(leftEntry, skipNavigation);
+ }
+ }
+
+ private void MarkManyToManyRelationshipAsLoaded(EntityEntry leftEntry, ISkipNavigation skipNavigation)
+ {
+ string[] primaryKeyNames = skipNavigation.ForeignKey.PrincipalKey.Properties.Select(property => property.Name).ToArray();
+ object?[] primaryKeyValues = GetCurrentKeyValues(leftEntry, primaryKeyNames);
+
+ string[] foreignKeyNames = skipNavigation.ForeignKey.Properties.Select(property => property.Name).ToArray();
+
+ foreach (EntityEntry joinEntry in _dbContext.ChangeTracker.Entries().Where(entry => entry.Metadata == skipNavigation.JoinEntityType).ToList())
+ {
+ object?[] foreignKeyValues = GetCurrentKeyValues(joinEntry, foreignKeyNames);
+
+ if (primaryKeyValues.SequenceEqual(foreignKeyValues))
+ {
+ joinEntry.State = EntityState.Unchanged;
+ }
+ }
+ }
+
+ private static object?[] GetCurrentKeyValues(EntityEntry entry, IEnumerable keyNames)
+ {
+ return keyNames.Select(keyName => entry.Property(keyName).CurrentValue).ToArray();
}
protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship, TResource leftResource, object? valueToAssign,
diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/SingleOrManyDataConverterFactory.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/SingleOrManyDataConverterFactory.cs
index 25e497c2c1..b842cace0e 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonConverters/SingleOrManyDataConverterFactory.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/SingleOrManyDataConverterFactory.cs
@@ -26,9 +26,9 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer
}
private sealed class SingleOrManyDataConverter : JsonObjectConverter>
- where T : class, IResourceIdentity, new()
+ where T : ResourceIdentifierObject, new()
{
- public override SingleOrManyData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions serializerOptions)
+ public override SingleOrManyData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var objects = new List();
bool isManyData = false;
@@ -54,7 +54,7 @@ public override SingleOrManyData Read(ref Utf8JsonReader reader, Type typeToC
}
case JsonTokenType.StartObject:
{
- var resourceObject = ReadSubTree(ref reader, serializerOptions);
+ var resourceObject = ReadSubTree(ref reader, options);
objects.Add(resourceObject);
break;
}
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/AtomicReference.cs b/src/JsonApiDotNetCore/Serialization/Objects/AtomicReference.cs
index 01693d1db6..fcc56298c1 100644
--- a/src/JsonApiDotNetCore/Serialization/Objects/AtomicReference.cs
+++ b/src/JsonApiDotNetCore/Serialization/Objects/AtomicReference.cs
@@ -7,20 +7,8 @@ namespace JsonApiDotNetCore.Serialization.Objects;
/// See "ref" in https://jsonapi.org/ext/atomic/#operation-objects.
///
[PublicAPI]
-public sealed class AtomicReference : IResourceIdentity
+public sealed class AtomicReference : ResourceIdentity
{
- [JsonPropertyName("type")]
- [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
- public string? Type { get; set; }
-
- [JsonPropertyName("id")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Id { get; set; }
-
- [JsonPropertyName("lid")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Lid { get; set; }
-
[JsonPropertyName("relationship")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Relationship { get; set; }
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/IResourceIdentity.cs b/src/JsonApiDotNetCore/Serialization/Objects/IResourceIdentity.cs
deleted file mode 100644
index c4b57f535f..0000000000
--- a/src/JsonApiDotNetCore/Serialization/Objects/IResourceIdentity.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace JsonApiDotNetCore.Serialization.Objects;
-
-public interface IResourceIdentity
-{
- public string? Type { get; }
- public string? Id { get; }
- public string? Lid { get; }
-}
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs
index a1b8271cf7..20c30909ed 100644
--- a/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs
+++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs
@@ -7,21 +7,10 @@ namespace JsonApiDotNetCore.Serialization.Objects;
/// See https://jsonapi.org/format/1.1/#document-resource-identifier-objects.
///
[PublicAPI]
-public sealed class ResourceIdentifierObject : IResourceIdentity
+public class ResourceIdentifierObject : ResourceIdentity
{
- [JsonPropertyName("type")]
- [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
- public string? Type { get; set; }
-
- [JsonPropertyName("id")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Id { get; set; }
-
- [JsonPropertyName("lid")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Lid { get; set; }
-
[JsonPropertyName("meta")]
+ [JsonPropertyOrder(100)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IDictionary? Meta { get; set; }
}
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentity.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentity.cs
new file mode 100644
index 0000000000..41a3d951e6
--- /dev/null
+++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentity.cs
@@ -0,0 +1,26 @@
+using System.Text.Json.Serialization;
+using JetBrains.Annotations;
+
+namespace JsonApiDotNetCore.Serialization.Objects;
+
+///
+/// Shared identity information for various JSON:API objects.
+///
+[PublicAPI]
+public abstract class ResourceIdentity
+{
+ [JsonPropertyName("type")]
+ [JsonPropertyOrder(-3)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
+ public string? Type { get; set; }
+
+ [JsonPropertyName("id")]
+ [JsonPropertyOrder(-2)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Id { get; set; }
+
+ [JsonPropertyName("lid")]
+ [JsonPropertyOrder(-1)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Lid { get; set; }
+}
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs
index 43b3b9616a..ed38a40f9a 100644
--- a/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs
+++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs
@@ -7,33 +7,20 @@ namespace JsonApiDotNetCore.Serialization.Objects;
/// See https://jsonapi.org/format/1.1/#document-resource-objects.
///
[PublicAPI]
-public sealed class ResourceObject : IResourceIdentity
+public sealed class ResourceObject : ResourceIdentifierObject
{
- [JsonPropertyName("type")]
- [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
- public string? Type { get; set; }
-
- [JsonPropertyName("id")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Id { get; set; }
-
- [JsonPropertyName("lid")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string? Lid { get; set; }
-
[JsonPropertyName("attributes")]
+ [JsonPropertyOrder(1)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IDictionary? Attributes { get; set; }
[JsonPropertyName("relationships")]
+ [JsonPropertyOrder(2)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IDictionary? Relationships { get; set; }
[JsonPropertyName("links")]
+ [JsonPropertyOrder(3)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public ResourceLinks? Links { get; set; }
-
- [JsonPropertyName("meta")]
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public IDictionary? Meta { get; set; }
}
diff --git a/src/JsonApiDotNetCore/Serialization/Objects/SingleOrManyData.cs b/src/JsonApiDotNetCore/Serialization/Objects/SingleOrManyData.cs
index 1d2f99e126..1126f84f26 100644
--- a/src/JsonApiDotNetCore/Serialization/Objects/SingleOrManyData.cs
+++ b/src/JsonApiDotNetCore/Serialization/Objects/SingleOrManyData.cs
@@ -13,7 +13,7 @@ namespace JsonApiDotNetCore.Serialization.Objects;
public readonly struct SingleOrManyData
// The "new()" constraint exists for parity with SingleOrManyDataConverterFactory, which creates empty instances
// to ensure ManyValue never contains null items.
- where T : class, IResourceIdentity, new()
+ where T : ResourceIdentifierObject, new()
{
public object? Value => ManyValue != null ? ManyValue : SingleValue;
diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/BaseAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/BaseAdapter.cs
index 64e2f6d53b..fb1111bea1 100644
--- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/BaseAdapter.cs
+++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/BaseAdapter.cs
@@ -11,7 +11,7 @@ public abstract class BaseAdapter
{
[AssertionMethod]
protected static void AssertHasData(SingleOrManyData data, RequestAdapterState state)
- where T : class, IResourceIdentity, new()
+ where T : ResourceIdentifierObject, new()
{
if (!data.IsAssigned)
{
@@ -21,7 +21,7 @@ protected static void AssertHasData(SingleOrManyData data, RequestAdapterS
[AssertionMethod]
protected static void AssertDataHasSingleValue(SingleOrManyData data, bool allowNull, RequestAdapterState state)
- where T : class, IResourceIdentity, new()
+ where T : ResourceIdentifierObject, new()
{
if (data.SingleValue == null)
{
@@ -44,7 +44,7 @@ protected static void AssertDataHasSingleValue(SingleOrManyData data, bool
[AssertionMethod]
protected static void AssertDataHasManyValue(SingleOrManyData data, RequestAdapterState state)
- where T : class, IResourceIdentity, new()
+ where T : ResourceIdentifierObject, new()
{
if (data.ManyValue == null)
{
diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs
index d163eb56d1..61c6cc1857 100644
--- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs
+++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs
@@ -25,7 +25,7 @@ protected ResourceIdentityAdapter(IResourceGraph resourceGraph, IResourceFactory
_resourceFactory = resourceFactory;
}
- protected (IIdentifiable resource, ResourceType resourceType) ConvertResourceIdentity(IResourceIdentity identity, ResourceIdentityRequirements requirements,
+ protected (IIdentifiable resource, ResourceType resourceType) ConvertResourceIdentity(ResourceIdentity identity, ResourceIdentityRequirements requirements,
RequestAdapterState state)
{
ArgumentGuard.NotNull(identity, nameof(identity));
@@ -38,7 +38,7 @@ protected ResourceIdentityAdapter(IResourceGraph resourceGraph, IResourceFactory
return (resource, resourceType);
}
- private ResourceType ResolveType(IResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state)
+ private ResourceType ResolveType(ResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state)
{
AssertHasType(identity.Type, state);
@@ -93,7 +93,7 @@ private static void AssertIsCompatibleResourceType(ResourceType actual, Resource
}
}
- private IIdentifiable CreateResource(IResourceIdentity identity, ResourceIdentityRequirements requirements, Type resourceClrType, RequestAdapterState state)
+ private IIdentifiable CreateResource(ResourceIdentity identity, ResourceIdentityRequirements requirements, Type resourceClrType, RequestAdapterState state)
{
if (state.Request.Kind != EndpointKind.AtomicOperations)
{
@@ -120,7 +120,7 @@ private IIdentifiable CreateResource(IResourceIdentity identity, ResourceIdentit
return resource;
}
- private static void AssertHasNoLid(IResourceIdentity identity, RequestAdapterState state)
+ private static void AssertHasNoLid(ResourceIdentity identity, RequestAdapterState state)
{
if (identity.Lid != null)
{
@@ -129,7 +129,7 @@ private static void AssertHasNoLid(IResourceIdentity identity, RequestAdapterSta
}
}
- private static void AssertNoIdWithLid(IResourceIdentity identity, RequestAdapterState state)
+ private static void AssertNoIdWithLid(ResourceIdentity identity, RequestAdapterState state)
{
if (identity.Id != null && identity.Lid != null)
{
@@ -137,7 +137,7 @@ private static void AssertNoIdWithLid(IResourceIdentity identity, RequestAdapter
}
}
- private static void AssertHasIdOrLid(IResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state)
+ private static void AssertHasIdOrLid(ResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state)
{
string? message = null;
@@ -160,7 +160,7 @@ private static void AssertHasIdOrLid(IResourceIdentity identity, ResourceIdentit
}
}
- private static void AssertHasNoId(IResourceIdentity identity, RequestAdapterState state)
+ private static void AssertHasNoId(ResourceIdentity identity, RequestAdapterState state)
{
if (identity.Id != null)
{
@@ -169,7 +169,7 @@ private static void AssertHasNoId(IResourceIdentity identity, RequestAdapterStat
}
}
- private static void AssertSameIdValue(IResourceIdentity identity, string? expected, RequestAdapterState state)
+ private static void AssertSameIdValue(ResourceIdentity identity, string? expected, RequestAdapterState state)
{
if (expected != null && identity.Id != expected)
{
@@ -180,7 +180,7 @@ private static void AssertSameIdValue(IResourceIdentity identity, string? expect
}
}
- private static void AssertSameLidValue(IResourceIdentity identity, string? expected, RequestAdapterState state)
+ private static void AssertSameLidValue(ResourceIdentity identity, string? expected, RequestAdapterState state)
{
if (expected != null && identity.Lid != expected)
{
@@ -191,7 +191,7 @@ private static void AssertSameLidValue(IResourceIdentity identity, string? expec
}
}
- private void AssignStringId(IResourceIdentity identity, IIdentifiable resource, RequestAdapterState state)
+ private void AssignStringId(ResourceIdentity identity, IIdentifiable resource, RequestAdapterState state)
{
if (identity.Id != null)
{
diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs
index 11db5e8ee3..d5498397bf 100644
--- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs
+++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs
@@ -5,7 +5,7 @@
namespace JsonApiDotNetCore.Serialization.Request.Adapters;
///
-/// Defines requirements to validate an instance against.
+/// Defines requirements to validate a instance against.
///
[PublicAPI]
public sealed class ResourceIdentityRequirements
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
index 29213f5e69..d61d378db7 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
@@ -47,4 +47,7 @@ public override string? Id
[HasOne]
public Dealership? Dealership { get; set; }
+
+ [HasMany]
+ public ISet PreviousDealerships { get; set; } = new HashSet();
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs
index 67213057b7..e9e12439e6 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs
@@ -34,5 +34,9 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity()
.HasMany(dealership => dealership.Inventory)
.WithOne(car => car.Dealership!);
+
+ builder.Entity()
+ .HasMany(car => car.PreviousDealerships)
+ .WithMany(dealership => dealership.SoldCars);
}
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs
index 185367930d..9e273be36e 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs
@@ -508,4 +508,52 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
carInDatabase.Should().BeNull();
});
}
+
+ [Fact]
+ public async Task Can_remove_from_ManyToMany_relationship()
+ {
+ // Arrange
+ Dealership existingDealership = _fakers.Dealership.Generate();
+ existingDealership.SoldCars = _fakers.Car.Generate(2).ToHashSet();
+
+ await _testContext.RunOnDatabaseAsync(async dbContext =>
+ {
+ await dbContext.ClearTableAsync();
+ dbContext.Dealerships.Add(existingDealership);
+ await dbContext.SaveChangesAsync();
+ });
+
+ var requestBody = new
+ {
+ data = new[]
+ {
+ new
+ {
+ type = "cars",
+ id = existingDealership.SoldCars.ElementAt(1).StringId
+ }
+ }
+ };
+
+ string route = $"/dealerships/{existingDealership.StringId}/relationships/soldCars";
+
+ // Act
+ (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecuteDeleteAsync(route, requestBody);
+
+ // Assert
+ httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent);
+
+ responseDocument.Should().BeEmpty();
+
+ await _testContext.RunOnDatabaseAsync(async dbContext =>
+ {
+ Dealership dealershipInDatabase = await dbContext.Dealerships.Include(dealership => dealership.SoldCars).FirstWithIdAsync(existingDealership.Id);
+
+ dealershipInDatabase.SoldCars.ShouldHaveCount(1);
+ dealershipInDatabase.SoldCars.Single().Id.Should().Be(existingDealership.SoldCars.ElementAt(0).Id);
+
+ List carsInDatabase = await dbContext.Cars.ToListAsync();
+ carsInDatabase.ShouldHaveCount(2);
+ });
+ }
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
index 14784cb438..091e7acbe1 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
@@ -13,4 +13,7 @@ public sealed class Dealership : Identifiable
[HasMany]
public ISet Inventory { get; set; } = new HashSet();
+
+ [HasMany]
+ public ISet SoldCars { get; set; } = new HashSet();
}
diff --git a/test/OpenApiClientTests/LegacyClient/swagger.g.json b/test/OpenApiClientTests/LegacyClient/swagger.g.json
index 03575fe1d2..f95214e8d4 100644
--- a/test/OpenApiClientTests/LegacyClient/swagger.g.json
+++ b/test/OpenApiClientTests/LegacyClient/swagger.g.json
@@ -58,7 +58,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -68,7 +68,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -166,7 +166,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -187,7 +187,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -333,7 +333,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -363,7 +363,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -393,7 +393,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -451,7 +451,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -461,7 +461,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -559,7 +559,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -580,7 +580,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -726,7 +726,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -756,7 +756,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -786,7 +786,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -932,7 +932,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -962,7 +962,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -992,7 +992,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1050,7 +1050,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -1060,7 +1060,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1158,7 +1158,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1179,7 +1179,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1325,7 +1325,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1471,7 +1471,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1501,7 +1501,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1531,7 +1531,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1677,7 +1677,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1707,7 +1707,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1737,7 +1737,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1883,7 +1883,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -2046,6 +2046,7 @@
"$ref": "#/components/schemas/airplane-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2087,6 +2088,7 @@
"$ref": "#/components/schemas/airplane-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2197,6 +2199,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2217,6 +2220,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2237,6 +2241,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2294,6 +2299,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2335,6 +2341,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2364,6 +2371,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2619,6 +2627,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2657,6 +2666,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2686,6 +2696,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2860,9 +2871,11 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2876,12 +2889,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -2903,6 +2918,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
@@ -2920,15 +2936,18 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -2951,12 +2970,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2969,6 +2990,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
}
},
@@ -3153,6 +3175,7 @@
"$ref": "#/components/schemas/passenger-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -3179,6 +3202,7 @@
"$ref": "#/components/schemas/passenger-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
diff --git a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json
index 2eaac9303c..d2850c032d 100644
--- a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json
+++ b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json
@@ -58,7 +58,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -68,7 +68,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -169,7 +169,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -191,7 +191,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -342,7 +342,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -493,7 +493,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -524,7 +524,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -555,7 +555,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -706,7 +706,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -747,9 +747,11 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -763,12 +765,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -790,6 +794,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
@@ -807,15 +812,18 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -838,12 +846,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -856,6 +866,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1040,6 +1051,7 @@
"$ref": "#/components/schemas/staffMemberResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1066,6 +1078,7 @@
"$ref": "#/components/schemas/staffMemberResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1225,6 +1238,7 @@
"$ref": "#/components/schemas/supermarketResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1266,6 +1280,7 @@
"$ref": "#/components/schemas/supermarketResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
diff --git a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json
index 1013e900a8..1d7feb51f0 100644
--- a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json
+++ b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json
@@ -58,7 +58,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -68,7 +68,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -169,7 +169,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -191,7 +191,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -342,7 +342,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -493,7 +493,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -524,7 +524,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -555,7 +555,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -706,7 +706,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -747,9 +747,11 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -763,12 +765,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -790,6 +794,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
@@ -807,15 +812,18 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -838,12 +846,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -856,6 +866,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1040,6 +1051,7 @@
"$ref": "#/components/schemas/staff-member-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1066,6 +1078,7 @@
"$ref": "#/components/schemas/staff-member-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1225,6 +1238,7 @@
"$ref": "#/components/schemas/supermarket-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1266,6 +1280,7 @@
"$ref": "#/components/schemas/supermarket-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
diff --git a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json
index 3906897de4..fa5041afad 100644
--- a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json
+++ b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json
@@ -58,7 +58,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -68,7 +68,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -169,7 +169,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -191,7 +191,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -342,7 +342,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -493,7 +493,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -524,7 +524,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -555,7 +555,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -706,7 +706,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -747,9 +747,11 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -763,12 +765,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -790,6 +794,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
@@ -807,15 +812,18 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -838,12 +846,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -856,6 +866,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1040,6 +1051,7 @@
"$ref": "#/components/schemas/StaffMemberResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1066,6 +1078,7 @@
"$ref": "#/components/schemas/StaffMemberResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -1225,6 +1238,7 @@
"$ref": "#/components/schemas/SupermarketResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -1266,6 +1280,7 @@
"$ref": "#/components/schemas/SupermarketResourceType"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj
index 3f54c161b8..41e6fe07e1 100644
--- a/test/OpenApiClientTests/OpenApiClientTests.csproj
+++ b/test/OpenApiClientTests/OpenApiClientTests.csproj
@@ -18,16 +18,12 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json
index 3fb80fb856..4b6c140fc4 100644
--- a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json
+++ b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json
@@ -58,7 +58,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -68,7 +68,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -166,7 +166,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -187,7 +187,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -333,7 +333,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -363,7 +363,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -393,7 +393,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -451,7 +451,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -461,7 +461,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -559,7 +559,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -580,7 +580,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -726,7 +726,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -756,7 +756,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -786,7 +786,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -932,7 +932,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -962,7 +962,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -992,7 +992,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1050,7 +1050,7 @@
},
"responses": {
"201": {
- "description": "Success",
+ "description": "Created",
"content": {
"application/vnd.api+json": {
"schema": {
@@ -1060,7 +1060,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1158,7 +1158,7 @@
}
},
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1179,7 +1179,7 @@
],
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1325,7 +1325,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1471,7 +1471,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1501,7 +1501,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1531,7 +1531,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1677,7 +1677,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1707,7 +1707,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
},
@@ -1737,7 +1737,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -1883,7 +1883,7 @@
},
"responses": {
"204": {
- "description": "Success"
+ "description": "No Content"
}
}
}
@@ -2046,6 +2046,7 @@
"$ref": "#/components/schemas/airplane-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2087,6 +2088,7 @@
"$ref": "#/components/schemas/airplane-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2197,6 +2199,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2217,6 +2220,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2237,6 +2241,7 @@
"type": "object",
"properties": {
"email-address": {
+ "minLength": 1,
"type": "string",
"format": "email"
},
@@ -2294,6 +2299,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2335,6 +2341,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2364,6 +2371,7 @@
"$ref": "#/components/schemas/flight-attendant-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2619,6 +2627,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2657,6 +2666,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -2686,6 +2696,7 @@
"$ref": "#/components/schemas/flight-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2860,9 +2871,11 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2876,12 +2889,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -2903,6 +2918,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
@@ -2920,15 +2936,18 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
},
"first": {
+ "minLength": 1,
"type": "string"
},
"last": {
@@ -2951,12 +2970,14 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
},
"describedby": {
"type": "string"
},
"related": {
+ "minLength": 1,
"type": "string"
}
},
@@ -2969,6 +2990,7 @@
"type": "object",
"properties": {
"self": {
+ "minLength": 1,
"type": "string"
}
},
@@ -3153,6 +3175,7 @@
"$ref": "#/components/schemas/passenger-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
},
"attributes": {
@@ -3179,6 +3202,7 @@
"$ref": "#/components/schemas/passenger-resource-type"
},
"id": {
+ "minLength": 1,
"type": "string"
}
},
@@ -3360,4 +3384,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj
index f5a24feec9..50b96c1d36 100644
--- a/test/OpenApiTests/OpenApiTests.csproj
+++ b/test/OpenApiTests/OpenApiTests.csproj
@@ -23,6 +23,6 @@
-
+
diff --git a/test/SourceGeneratorTests/ControllerGenerationTests.cs b/test/SourceGeneratorTests/ControllerGenerationTests.cs
index 614b4d316c..9f5f9f83d3 100644
--- a/test/SourceGeneratorTests/ControllerGenerationTests.cs
+++ b/test/SourceGeneratorTests/ControllerGenerationTests.cs
@@ -51,7 +51,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -111,7 +113,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -171,7 +175,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -234,7 +240,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -574,7 +582,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -633,7 +643,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
@@ -691,7 +703,9 @@ public sealed class Item : Identifiable
GeneratorDriverRunResult runResult = driver.GetRunResult();
runResult.Should().NotHaveDiagnostics();
- runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging;
+ runResult.Should().HaveProducedSourceCode(@"//
+
+using Microsoft.Extensions.Logging;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj
index 2f383d72e9..ed335f630f 100644
--- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj
+++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj
@@ -16,7 +16,7 @@
-
+