From 6b69ed3f8f396348b1665e6395acfb77ab74c4bd Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 9 Sep 2022 14:36:27 +0200 Subject: [PATCH 01/47] Fixed error in documentation example --- docs/usage/extensibility/controllers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/extensibility/controllers.md b/docs/usage/extensibility/controllers.md index 0c71f45090..7e54d3fb9c 100644 --- a/docs/usage/extensibility/controllers.md +++ b/docs/usage/extensibility/controllers.md @@ -39,7 +39,7 @@ DELETE http://localhost:14140/articles/1 HTTP/1.1 ```json { "links": { - "self": "/articles" + "self": "/articles/1" }, "errors": [ { From 79d918a7a442de304205a5195cd707215d472a93 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 1 Oct 2022 12:41:40 +0200 Subject: [PATCH 02/47] Update links to JSON:API v1.1-final --- README.md | 2 +- docs/usage/reading/including-relationships.md | 2 +- src/JsonApiDotNetCore/Serialization/Objects/Document.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/ErrorLinks.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/ErrorObject.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/ErrorSource.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs | 2 +- .../Serialization/Objects/RelationshipLinks.cs | 2 +- .../Serialization/Objects/RelationshipObject.cs | 2 +- .../Serialization/Objects/ResourceIdentifierObject.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/ResourceLinks.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs | 2 +- src/JsonApiDotNetCore/Serialization/Objects/TopLevelLinks.cs | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e2291f080e..35adb3fe9f 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ These are some steps you can take to help you understand what this project is an - [Embercasts: Full Stack Ember with ASP.NET Core](https://www.embercasts.com/course/full-stack-ember-with-dotnet/watch/whats-in-this-course-cs) (paid course, 2017) ### Official documentation -- [The JSON:API specification](https://jsonapi.org/format/1.1/) +- [The JSON:API specification](https://jsonapi.org/format/) - [JsonApiDotNetCore website](https://www.jsonapi.net/) - [Roadmap](ROADMAP.md) diff --git a/docs/usage/reading/including-relationships.md b/docs/usage/reading/including-relationships.md index f22d2321aa..0b69a007c1 100644 --- a/docs/usage/reading/including-relationships.md +++ b/docs/usage/reading/including-relationships.md @@ -1,6 +1,6 @@ # Including Relationships -JsonApiDotNetCore supports [request include params](http://jsonapi.org/format/#fetching-includes) out of the box, +JsonApiDotNetCore supports [request include params](https://jsonapi.org/format/#fetching-includes) out of the box, for side-loading related resources. ```http diff --git a/src/JsonApiDotNetCore/Serialization/Objects/Document.cs b/src/JsonApiDotNetCore/Serialization/Objects/Document.cs index 2f40aeb27b..e0d7d5def3 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/Document.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/Document.cs @@ -3,7 +3,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-top-level and https://jsonapi.org/ext/atomic/#document-structure. +/// See https://jsonapi.org/format#document-top-level and https://jsonapi.org/ext/atomic/#document-structure. /// public sealed class Document { diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ErrorLinks.cs b/src/JsonApiDotNetCore/Serialization/Objects/ErrorLinks.cs index 4b8d4de528..6ba2c2a6f6 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ErrorLinks.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ErrorLinks.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See "links" in https://jsonapi.org/format/1.1/#error-objects. +/// See "links" in https://jsonapi.org/format/#error-objects. /// [PublicAPI] public sealed class ErrorLinks diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ErrorObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/ErrorObject.cs index 87ad1ebefe..68a2a19d3a 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ErrorObject.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ErrorObject.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#error-objects. +/// See https://jsonapi.org/format/#error-objects. /// [PublicAPI] public sealed class ErrorObject diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ErrorSource.cs b/src/JsonApiDotNetCore/Serialization/Objects/ErrorSource.cs index 156f734aa2..b9242895f4 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ErrorSource.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ErrorSource.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See "source" in https://jsonapi.org/format/1.1/#error-objects. +/// See "source" in https://jsonapi.org/format/#error-objects. /// [PublicAPI] public sealed class ErrorSource diff --git a/src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs index 4a48f90099..66daec22ff 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-jsonapi-object. +/// See https://jsonapi.org/format/#document-jsonapi-object. /// [PublicAPI] public sealed class JsonApiObject diff --git a/src/JsonApiDotNetCore/Serialization/Objects/RelationshipLinks.cs b/src/JsonApiDotNetCore/Serialization/Objects/RelationshipLinks.cs index f3f6c2bf02..4c1095e1a7 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/RelationshipLinks.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/RelationshipLinks.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See "links" in https://jsonapi.org/format/1.1/#document-resource-object-relationships. +/// See "links" in https://jsonapi.org/format/#document-resource-object-relationships. /// [PublicAPI] public sealed class RelationshipLinks diff --git a/src/JsonApiDotNetCore/Serialization/Objects/RelationshipObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/RelationshipObject.cs index 9411ecf83a..c677e9a0fb 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/RelationshipObject.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/RelationshipObject.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-resource-object-relationships. +/// See https://jsonapi.org/format/#document-resource-object-relationships. /// [PublicAPI] public sealed class RelationshipObject diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs index 20c30909ed..e82ebe16bf 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceIdentifierObject.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-resource-identifier-objects. +/// See https://jsonapi.org/format/#document-resource-identifier-objects. /// [PublicAPI] public class ResourceIdentifierObject : ResourceIdentity diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceLinks.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceLinks.cs index 22c396082d..6f749cca88 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ResourceLinks.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceLinks.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-resource-object-links. +/// See https://jsonapi.org/format/#document-resource-object-links. /// [PublicAPI] public sealed class ResourceLinks diff --git a/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs index ed38a40f9a..fc1ff3d146 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/ResourceObject.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See https://jsonapi.org/format/1.1/#document-resource-objects. +/// See https://jsonapi.org/format/#document-resource-objects. /// [PublicAPI] public sealed class ResourceObject : ResourceIdentifierObject diff --git a/src/JsonApiDotNetCore/Serialization/Objects/TopLevelLinks.cs b/src/JsonApiDotNetCore/Serialization/Objects/TopLevelLinks.cs index 14578253c2..f83510fb23 100644 --- a/src/JsonApiDotNetCore/Serialization/Objects/TopLevelLinks.cs +++ b/src/JsonApiDotNetCore/Serialization/Objects/TopLevelLinks.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Serialization.Objects; /// -/// See "links" in https://jsonapi.org/format/1.1/#document-top-level. +/// See "links" in https://jsonapi.org/format/#document-top-level. /// [PublicAPI] public sealed class TopLevelLinks From 1dc249bf5c8f46216a871913f6d9931a91c59525 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 28 Sep 2022 04:39:53 +0200 Subject: [PATCH 03/47] Improve error message when duplicate controllers are found, to include the fully qualified type names of the conflicting controllers --- src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs | 3 ++- .../DuplicateResourceControllerTests.cs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs index fe95d93446..58f8d18560 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs @@ -78,7 +78,8 @@ public void Apply(ApplicationModel application) { if (_controllerPerResourceTypeMap.ContainsKey(resourceType)) { - throw new InvalidConfigurationException($"Multiple controllers found for resource type '{resourceType}'."); + throw new InvalidConfigurationException( + $"Multiple controllers found for resource type '{resourceType}': '{_controllerPerResourceTypeMap[resourceType].ControllerType}' and '{controller.ControllerType}'."); } _resourceTypePerControllerTypeMap.Add(controller.ControllerType, resourceType); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs index b130523588..0f1b1178f4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs @@ -20,6 +20,9 @@ public void Fails_at_startup_when_multiple_controllers_exist_for_same_resource_t Action action = () => _ = Factory; // Assert - action.Should().ThrowExactly().WithMessage("Multiple controllers found for resource type 'knownResources'."); + InvalidConfigurationException exception = action.Should().ThrowExactly().Which!; + exception.Message.Should().StartWith("Multiple controllers found for resource type 'knownResources': "); + exception.Message.Should().Contain($"'{typeof(KnownResourcesController).FullName}'"); + exception.Message.Should().Contain($"'{typeof(DuplicateKnownResourcesController).FullName}'"); } } From 70c06b941d2201c53c8e0940cd4cf0420cfa40df Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 28 Sep 2022 03:14:21 +0200 Subject: [PATCH 04/47] Multi-target Annotations against .NET Standard 1.0 --- JsonApiDotNetCore.sln | 15 +++++++++++++ ...ndpoints.cs => JsonApiEndpoints.shared.cs} | 0 .../JsonApiDotNetCore.Annotations.csproj | 22 +++++++++++++++++-- .../Annotations/AttrAttribute.netstandard.cs | 14 ++++++++++++ ...bilities.cs => AttrCapabilities.shared.cs} | 3 +++ .../EagerLoadAttribute.netstandard.cs | 12 ++++++++++ .../HasManyAttribute.netstandard.cs | 12 ++++++++++ .../HasOneAttribute.netstandard.cs | 12 ++++++++++ .../{LinkTypes.cs => LinkTypes.shared.cs} | 0 ...ibute.cs => NoResourceAttribute.shared.cs} | 0 .../RelationshipAttribute.netstandard.cs | 16 ++++++++++++++ ...tribute.cs => ResourceAttribute.shared.cs} | 0 .../ResourceFieldAttribute.netstandard.cs | 13 +++++++++++ ...te.cs => ResourceLinksAttribute.shared.cs} | 0 ...dentifiable.cs => IIdentifiable.shared.cs} | 4 ++++ .../Resources/Identifiable.netstandard.cs | 16 ++++++++++++++ test/AnnotationTests/AnnotationTests.csproj | 20 +++++++++++++++++ test/AnnotationTests/Models/HiddenNode.cs | 14 ++++++++++++ test/AnnotationTests/Models/TreeNode.cs | 20 +++++++++++++++++ 19 files changed, 191 insertions(+), 2 deletions(-) rename src/JsonApiDotNetCore.Annotations/Controllers/{JsonApiEndpoints.cs => JsonApiEndpoints.shared.cs} (100%) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.netstandard.cs rename src/JsonApiDotNetCore.Annotations/Resources/Annotations/{AttrCapabilities.cs => AttrCapabilities.shared.cs} (96%) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/EagerLoadAttribute.netstandard.cs create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs rename src/JsonApiDotNetCore.Annotations/Resources/Annotations/{LinkTypes.cs => LinkTypes.shared.cs} (100%) rename src/JsonApiDotNetCore.Annotations/Resources/Annotations/{NoResourceAttribute.cs => NoResourceAttribute.shared.cs} (100%) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs rename src/JsonApiDotNetCore.Annotations/Resources/Annotations/{ResourceAttribute.cs => ResourceAttribute.shared.cs} (100%) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.netstandard.cs rename src/JsonApiDotNetCore.Annotations/Resources/Annotations/{ResourceLinksAttribute.cs => ResourceLinksAttribute.shared.cs} (100%) rename src/JsonApiDotNetCore.Annotations/Resources/{IIdentifiable.cs => IIdentifiable.shared.cs} (94%) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Identifiable.netstandard.cs create mode 100644 test/AnnotationTests/AnnotationTests.csproj create mode 100644 test/AnnotationTests/Models/HiddenNode.cs create mode 100644 test/AnnotationTests/Models/TreeNode.cs diff --git a/JsonApiDotNetCore.sln b/JsonApiDotNetCore.sln index 21b1cca7ce..e034595875 100644 --- a/JsonApiDotNetCore.sln +++ b/JsonApiDotNetCore.sln @@ -52,6 +52,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.Annotatio EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DatabasePerTenantExample", "src\Examples\DatabasePerTenantExample\DatabasePerTenantExample.csproj", "{60334658-BE51-43B3-9C4D-F2BBF56C89CE}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnnotationTests", "test\AnnotationTests\AnnotationTests.csproj", "{24B0C12F-38CD-4245-8785-87BEFAD55B00}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -266,6 +268,18 @@ Global {60334658-BE51-43B3-9C4D-F2BBF56C89CE}.Release|x64.Build.0 = Release|Any CPU {60334658-BE51-43B3-9C4D-F2BBF56C89CE}.Release|x86.ActiveCfg = Release|Any CPU {60334658-BE51-43B3-9C4D-F2BBF56C89CE}.Release|x86.Build.0 = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|x64.ActiveCfg = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|x64.Build.0 = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|x86.ActiveCfg = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Debug|x86.Build.0 = Debug|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|Any CPU.Build.0 = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x64.ActiveCfg = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x64.Build.0 = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x86.ActiveCfg = Release|Any CPU + {24B0C12F-38CD-4245-8785-87BEFAD55B00}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -288,6 +302,7 @@ Global {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} {83FF097C-C8C6-477B-9FAB-DF99B84978B5} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF} {60334658-BE51-43B3-9C4D-F2BBF56C89CE} = {026FBC6C-AF76-4568-9B87-EC73457899FD} + {24B0C12F-38CD-4245-8785-87BEFAD55B00} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4} diff --git a/src/JsonApiDotNetCore.Annotations/Controllers/JsonApiEndpoints.cs b/src/JsonApiDotNetCore.Annotations/Controllers/JsonApiEndpoints.shared.cs similarity index 100% rename from src/JsonApiDotNetCore.Annotations/Controllers/JsonApiEndpoints.cs rename to src/JsonApiDotNetCore.Annotations/Controllers/JsonApiEndpoints.shared.cs diff --git a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj index 7c78f620ed..5f4d7c1d76 100644 --- a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj +++ b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj @@ -1,9 +1,10 @@ - + - $(TargetFrameworkName) + $(TargetFrameworkName);netstandard1.0 true true JsonApiDotNetCore + latest @@ -27,4 +28,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.netstandard.cs new file mode 100644 index 0000000000..a7915240dc --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.netstandard.cs @@ -0,0 +1,14 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +[AttributeUsage(AttributeTargets.Property)] +public sealed class AttrAttribute : ResourceFieldAttribute +{ + /// + public AttrCapabilities Capabilities { get; set; } +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs similarity index 96% rename from src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.cs rename to src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs index c6f849fdff..2812be6d39 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs @@ -1,8 +1,11 @@ +using JetBrains.Annotations; + namespace JsonApiDotNetCore.Resources.Annotations; /// /// Indicates capabilities that can be performed on an . /// +[PublicAPI] [Flags] public enum AttrCapabilities { diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/EagerLoadAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/EagerLoadAttribute.netstandard.cs new file mode 100644 index 0000000000..47052a078c --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/EagerLoadAttribute.netstandard.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +[AttributeUsage(AttributeTargets.Property)] +public sealed class EagerLoadAttribute : Attribute +{ +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs new file mode 100644 index 0000000000..1cdeb9f62f --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +[AttributeUsage(AttributeTargets.Property)] +public sealed class HasManyAttribute : RelationshipAttribute +{ +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs new file mode 100644 index 0000000000..1c16fb01b2 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +[AttributeUsage(AttributeTargets.Property)] +public sealed class HasOneAttribute : RelationshipAttribute +{ +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/LinkTypes.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/LinkTypes.shared.cs similarity index 100% rename from src/JsonApiDotNetCore.Annotations/Resources/Annotations/LinkTypes.cs rename to src/JsonApiDotNetCore.Annotations/Resources/Annotations/LinkTypes.shared.cs diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/NoResourceAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/NoResourceAttribute.shared.cs similarity index 100% rename from src/JsonApiDotNetCore.Annotations/Resources/Annotations/NoResourceAttribute.cs rename to src/JsonApiDotNetCore.Annotations/Resources/Annotations/NoResourceAttribute.shared.cs diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs new file mode 100644 index 0000000000..51517b3d51 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +public abstract class RelationshipAttribute : ResourceFieldAttribute +{ + /// + public LinkTypes Links { get; set; } = LinkTypes.NotConfigured; + + /// + public bool CanInclude { get; set; } = true; +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceAttribute.shared.cs similarity index 100% rename from src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceAttribute.cs rename to src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceAttribute.shared.cs diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.netstandard.cs new file mode 100644 index 0000000000..958794365d --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.netstandard.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +[PublicAPI] +public abstract class ResourceFieldAttribute : Attribute +{ + /// + public string PublicName { get; set; } = null!; +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceLinksAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceLinksAttribute.shared.cs similarity index 100% rename from src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceLinksAttribute.cs rename to src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceLinksAttribute.shared.cs diff --git a/src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.cs b/src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.shared.cs similarity index 94% rename from src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.cs rename to src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.shared.cs index 2c6dc02025..b6dc5e3b22 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/IIdentifiable.shared.cs @@ -1,8 +1,11 @@ +using JetBrains.Annotations; + namespace JsonApiDotNetCore.Resources; /// /// Defines the basic contract for a JSON:API resource. All resource classes must implement . /// +[PublicAPI] public interface IIdentifiable { /// @@ -22,6 +25,7 @@ public interface IIdentifiable /// /// The resource identifier type. /// +[PublicAPI] public interface IIdentifiable : IIdentifiable { /// diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Identifiable.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Identifiable.netstandard.cs new file mode 100644 index 0000000000..a5a7179af6 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Identifiable.netstandard.cs @@ -0,0 +1,16 @@ +namespace JsonApiDotNetCore.Resources; + +/// +/// A simplified version, provided for convenience to multi-target against NetStandard. Does not actually work with JsonApiDotNetCore. +/// +public abstract class Identifiable : IIdentifiable +{ + /// + public virtual TId Id { get; set; } = default!; + + /// + public string? StringId { get; set; } + + /// + public string? LocalId { get; set; } +} diff --git a/test/AnnotationTests/AnnotationTests.csproj b/test/AnnotationTests/AnnotationTests.csproj new file mode 100644 index 0000000000..51df20d735 --- /dev/null +++ b/test/AnnotationTests/AnnotationTests.csproj @@ -0,0 +1,20 @@ + + + $(TargetFrameworkName);netstandard1.0 + latest + + + + + PreserveNewest + + + + + + + + + + + diff --git a/test/AnnotationTests/Models/HiddenNode.cs b/test/AnnotationTests/Models/HiddenNode.cs new file mode 100644 index 0000000000..c348f3b5b0 --- /dev/null +++ b/test/AnnotationTests/Models/HiddenNode.cs @@ -0,0 +1,14 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace AnnotationTests.Models; + +[PublicAPI] +[NoResource] +[ResourceLinks(TopLevelLinks = LinkTypes.None, ResourceLinks = LinkTypes.None, RelationshipLinks = LinkTypes.None)] +public sealed class HiddenNode : Identifiable +{ + [EagerLoad] + public HiddenNode? Parent { get; set; } +} diff --git a/test/AnnotationTests/Models/TreeNode.cs b/test/AnnotationTests/Models/TreeNode.cs new file mode 100644 index 0000000000..955db81720 --- /dev/null +++ b/test/AnnotationTests/Models/TreeNode.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace AnnotationTests.Models; + +[PublicAPI] +[Resource(PublicName = "tree-node", ControllerNamespace = "Models", GenerateControllerEndpoints = JsonApiEndpoints.Query)] +public sealed class TreeNode : Identifiable +{ + [Attr(PublicName = "name", Capabilities = AttrCapabilities.AllowSort)] + public string? DisplayName { get; set; } + + [HasOne(PublicName = "orders", CanInclude = true, Links = LinkTypes.All)] + public TreeNode? Parent { get; set; } + + [HasMany(PublicName = "orders", CanInclude = true, Links = LinkTypes.All)] + public ISet Children { get; set; } = new HashSet(); +} From ba0278b2e678caa94a0003fc951407be2ebb13c4 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 00:45:00 +0200 Subject: [PATCH 05/47] Update to latest Resharper version (disabled in Ubuntu cibuild) --- .config/dotnet-tools.json | 2 +- Build.ps1 | 7 +++++-- .../Configuration/ResourceGraphBuilder.cs | 2 +- test/TestBuildingBlocks/IntegrationTest.cs | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 3bd80e6084..d914fc369d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2021.3.4", + "version": "2022.2.3", "commands": [ "jb" ] diff --git a/Build.ps1 b/Build.ps1 index 4a92feed89..daccb81df1 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -105,8 +105,11 @@ CheckLastExitCode dotnet build -c Release CheckLastExitCode -RunInspectCode -RunCleanupCode +# https://youtrack.jetbrains.com/issue/RSRP-488628/Breaking-InspectCode-fails-with-Roslyn-Worker-process-exited-unexpectedly-after-update +if ($env:APPVEYOR_BUILD_WORKER_IMAGE -ne 'Ubuntu') { + RunInspectCode + RunCleanupCode +} dotnet test -c Release --no-build --collect:"XPlat Code Coverage" CheckLastExitCode diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs index e07318c7f7..36eb9d37e8 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs @@ -317,7 +317,7 @@ private IReadOnlyCollection GetRelationships(Type resourc private void SetPublicName(ResourceFieldAttribute field, PropertyInfo property) { - // ReSharper disable once ConstantNullCoalescingCondition + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract field.PublicName ??= FormatPropertyName(property); } diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index 99ced55b01..4fb2e5cd26 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -79,8 +79,7 @@ public abstract class IntegrationTest private string? SerializeRequest(object? requestBody) { - return requestBody == null ? null : - requestBody is string stringRequestBody ? stringRequestBody : JsonSerializer.Serialize(requestBody, SerializerOptions); + return requestBody == null ? null : requestBody as string ?? JsonSerializer.Serialize(requestBody, SerializerOptions); } protected abstract HttpClient CreateClient(); From 4c61b4998861deb819b9cde19443f16560029d62 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 05:31:24 +0200 Subject: [PATCH 06/47] Revert workaround for bug that was fixed in recent Resharper version --- Build.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index daccb81df1..d8be7da5e6 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -8,8 +8,7 @@ function CheckLastExitCode { function RunInspectCode { $outputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'jetbrains-inspectcode-results.xml') - # passing --build instead of --no-build as workaround for https://youtrack.jetbrains.com/issue/RSRP-487054 - dotnet jb inspectcode JsonApiDotNetCore.sln --build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal + dotnet jb inspectcode JsonApiDotNetCore.sln --no-build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal CheckLastExitCode [xml]$xml = Get-Content "$outputPath" From add5bdad36bdd17a563bc7a8aa7302ae9ae9f85e Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 00:56:34 +0200 Subject: [PATCH 07/47] Reduce the severity of 'Introduce optional parameters' for non-private members to Hint level, because they tend to cause backwards-compatibility issues in publicly exposed signatures --- JsonApiDotNetCore.sln.DotSettings | 1 + WarningSeverities.DotSettings | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index 95c0c2b7b2..a40e86f270 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -54,6 +54,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$); WARNING WARNING WARNING + HINT WARNING DO_NOT_SHOW HINT diff --git a/WarningSeverities.DotSettings b/WarningSeverities.DotSettings index f4a9ae32e8..0d4eeba96f 100644 --- a/WarningSeverities.DotSettings +++ b/WarningSeverities.DotSettings @@ -87,7 +87,6 @@ WARNING WARNING WARNING - WARNING WARNING WARNING WARNING From 3a1e039674fd556e1dcab05d84e562fc9e688e57 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 01:28:45 +0200 Subject: [PATCH 08/47] Reduce the use of nameof() in parameter validations by using CallerArgumentExpressionAttribute instead --- JsonApiDotNetCore.sln.DotSettings | 19 ++++++- .../ArgumentGuard.cs | 32 ++++------- .../CollectionConverter.cs | 6 +- .../Configuration/ResourceType.cs | 16 +++--- .../Annotations/RelationshipAttribute.cs | 2 +- .../Annotations/ResourceFieldAttribute.cs | 8 +-- .../Internal/RuntimeTypeConverter.cs | 2 +- .../TypeExtensions.cs | 4 +- .../EntityFrameworkCoreTransaction.cs | 4 +- .../EntityFrameworkCoreTransactionFactory.cs | 4 +- .../AtomicOperations/LocalIdTracker.cs | 14 ++--- .../AtomicOperations/LocalIdValidator.cs | 6 +- .../OperationProcessorAccessor.cs | 4 +- .../AtomicOperations/OperationsProcessor.cs | 16 +++--- .../Processors/AddToRelationshipProcessor.cs | 4 +- .../Processors/CreateProcessor.cs | 6 +- .../Processors/DeleteProcessor.cs | 4 +- .../RemoveFromRelationshipProcessor.cs | 4 +- .../Processors/SetRelationshipProcessor.cs | 4 +- .../Processors/UpdateProcessor.cs | 4 +- .../RevertRequestStateOnDispose.cs | 2 +- src/JsonApiDotNetCore/CollectionExtensions.cs | 8 +-- .../ApplicationBuilderExtensions.cs | 2 +- .../InverseNavigationResolver.cs | 4 +- .../JsonApiApplicationBuilder.cs | 8 +-- .../Configuration/JsonApiValidationFilter.cs | 2 +- .../Configuration/ResourceDescriptor.cs | 4 +- .../Configuration/ResourceGraph.cs | 12 ++-- .../Configuration/ResourceGraphBuilder.cs | 8 +-- .../Configuration/ResourceNameFormatter.cs | 2 +- .../ServiceCollectionExtensions.cs | 8 +-- .../Configuration/ServiceDiscoveryFacade.cs | 8 +-- .../Configuration/TypeLocator.cs | 16 +++--- .../DisableQueryStringAttribute.cs | 2 +- .../Controllers/BaseJsonApiController.cs | 24 ++++---- .../BaseJsonApiOperationsController.cs | 14 ++--- .../Controllers/CoreJsonApiController.cs | 4 +- .../Diagnostics/AspNetCodeTimerSession.cs | 4 +- .../Diagnostics/CodeTimingSessionManager.cs | 2 +- .../Errors/InvalidModelStateException.cs | 20 +++---- .../Errors/JsonApiException.cs | 4 +- .../Errors/MissingResourceInRelationship.cs | 6 +- .../UnsuccessfulActionResultException.cs | 2 +- .../AsyncConvertEmptyActionResultFilter.cs | 4 +- .../Middleware/AsyncJsonApiExceptionFilter.cs | 4 +- .../AsyncQueryStringActionFilter.cs | 6 +- .../Middleware/ExceptionHandler.cs | 12 ++-- .../Middleware/HttpContextExtensions.cs | 4 +- .../Middleware/JsonApiInputFormatter.cs | 4 +- .../Middleware/JsonApiMiddleware.cs | 10 ++-- .../Middleware/JsonApiOutputFormatter.cs | 4 +- .../Middleware/JsonApiRequest.cs | 2 +- .../Middleware/JsonApiRoutingConvention.cs | 6 +- .../Queries/ExpressionInScope.cs | 2 +- .../Queries/Expressions/AnyExpression.cs | 4 +- .../Expressions/ComparisonExpression.cs | 4 +- .../Queries/Expressions/CountExpression.cs | 2 +- .../Queries/Expressions/HasExpression.cs | 2 +- .../Expressions/IncludeChainConverter.cs | 2 +- .../Expressions/IncludeElementExpression.cs | 4 +- .../Queries/Expressions/IncludeExpression.cs | 2 +- .../Queries/Expressions/IsTypeExpression.cs | 2 +- .../Expressions/LiteralConstantExpression.cs | 2 +- .../Queries/Expressions/LogicalExpression.cs | 4 +- .../Expressions/MatchTextExpression.cs | 4 +- .../Queries/Expressions/NotExpression.cs | 2 +- .../Expressions/PaginationExpression.cs | 2 +- .../PaginationQueryStringValueExpression.cs | 2 +- .../QueryStringParameterScopeExpression.cs | 2 +- .../Expressions/QueryableHandlerExpression.cs | 2 +- .../ResourceFieldChainExpression.cs | 4 +- .../Expressions/SortElementExpression.cs | 4 +- .../Queries/Expressions/SortExpression.cs | 2 +- .../Expressions/SparseFieldSetExpression.cs | 2 +- .../SparseFieldSetExpressionExtensions.cs | 8 +-- .../Expressions/SparseFieldTableExpression.cs | 2 +- .../Queries/FieldSelection.cs | 2 +- .../Queries/FieldSelectors.cs | 8 +-- .../Queries/Internal/EvaluatedIncludeCache.cs | 2 +- .../Queries/Internal/Parsing/FilterParser.cs | 4 +- .../Queries/Internal/Parsing/IncludeParser.cs | 4 +- .../Internal/Parsing/PaginationParser.cs | 2 +- .../QueryStringParameterScopeParser.cs | 2 +- .../Internal/Parsing/QueryTokenizer.cs | 2 +- .../Queries/Internal/Parsing/SortParser.cs | 2 +- .../Internal/Parsing/SparseFieldSetParser.cs | 2 +- .../Internal/Parsing/SparseFieldTypeParser.cs | 2 +- .../Queries/Internal/QueryLayerComposer.cs | 56 +++++++++---------- .../QueryableBuilding/IncludeClauseBuilder.cs | 6 +- .../LambdaParameterNameFactory.cs | 2 +- .../LambdaParameterNameScope.cs | 4 +- .../Internal/QueryableBuilding/LambdaScope.cs | 6 +- .../QueryableBuilding/LambdaScopeFactory.cs | 4 +- .../QueryableBuilding/OrderClauseBuilder.cs | 6 +- .../QueryableBuilding/QueryClauseBuilder.cs | 6 +- .../QueryableBuilding/QueryableBuilder.cs | 14 ++--- .../QueryableBuilding/SelectClauseBuilder.cs | 14 ++--- .../SkipTakeClauseBuilder.cs | 6 +- .../QueryableBuilding/WhereClauseBuilder.cs | 8 +-- .../Queries/Internal/SparseFieldSetCache.cs | 10 ++-- src/JsonApiDotNetCore/Queries/QueryLayer.cs | 2 +- .../FilterQueryStringParameterReader.cs | 6 +- .../IncludeQueryStringParameterReader.cs | 4 +- .../Internal/LegacyFilterNotationConverter.cs | 6 +- .../PaginationQueryStringParameterReader.cs | 4 +- .../Internal/QueryStringParameterReader.cs | 4 +- .../Internal/QueryStringReader.cs | 8 +-- .../Internal/RequestQueryStringAccessor.cs | 2 +- ...ourceDefinitionQueryableParameterReader.cs | 4 +- .../SortQueryStringParameterReader.cs | 4 +- ...parseFieldSetQueryStringParameterReader.cs | 4 +- .../Repositories/DbContextExtensions.cs | 10 ++-- .../Repositories/DbContextResolver.cs | 2 +- .../EntityFrameworkCoreRepository.cs | 36 ++++++------ .../ResourceRepositoryAccessor.cs | 8 +-- .../Resources/AbstractResourceWrapper.cs | 2 +- .../Resources/IdentifiableExtensions.cs | 4 +- .../Resources/JsonApiResourceDefinition.cs | 4 +- .../Resources/OperationContainer.cs | 8 +-- .../Resources/ResourceChangeTracker.cs | 10 ++-- .../Resources/ResourceDefinitionAccessor.cs | 50 ++++++++--------- .../Resources/ResourceFactory.cs | 6 +- .../SortExpressionLambdaConverter.cs | 4 +- .../JsonConverters/ResourceObjectConverter.cs | 2 +- .../Adapters/AtomicOperationObjectAdapter.cs | 8 +-- .../Adapters/AtomicReferenceAdapter.cs | 6 +- .../Request/Adapters/AtomicReferenceResult.cs | 4 +- .../Request/Adapters/DocumentAdapter.cs | 10 ++-- .../DocumentInOperationsRequestAdapter.cs | 6 +- ...tInResourceOrRelationshipRequestAdapter.cs | 6 +- .../Adapters/RelationshipDataAdapter.cs | 6 +- .../Adapters/RequestAdapterPosition.cs | 2 +- .../Request/Adapters/RequestAdapterState.cs | 4 +- .../Request/Adapters/ResourceDataAdapter.cs | 8 +-- .../ResourceIdentifierObjectAdapter.cs | 6 +- .../Adapters/ResourceIdentityAdapter.cs | 10 ++-- .../Request/Adapters/ResourceObjectAdapter.cs | 10 ++-- .../Serialization/Request/JsonApiReader.cs | 8 +-- .../Request/JsonInvalidAttributeInfo.cs | 4 +- .../Request/ModelConversionException.cs | 2 +- .../Serialization/Response/ETagGenerator.cs | 2 +- .../Response/FingerprintGenerator.cs | 2 +- .../Serialization/Response/JsonApiWriter.cs | 14 ++--- .../Serialization/Response/LinkBuilder.cs | 18 +++--- .../Serialization/Response/MetaBuilder.cs | 8 +-- .../Response/ResourceObjectTreeNode.cs | 14 ++--- .../Response/ResponseModelAdapter.cs | 16 +++--- .../Services/AsyncCollectionExtensions.cs | 6 +- .../Services/JsonApiResourceService.cs | 32 +++++------ .../MusicTrackReleaseDefinition.cs | 2 +- .../EagerLoading/BuildingDefinition.cs | 2 +- .../HitCountingResourceDefinition.cs | 2 +- .../InjectionDbContext.cs | 2 +- .../InjectionFakers.cs | 2 +- .../ResourceTypeCapturingDefinition.cs | 4 +- .../WheelSortDefinition.cs | 2 +- .../DependencyContainerRegistrationTests.cs | 6 +- .../ServiceCollectionExtensions.cs | 2 +- .../ResourceConstructionExpressionTests.cs | 2 +- 159 files changed, 529 insertions(+), 528 deletions(-) diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index a40e86f270..1ffdf4a909 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -600,12 +600,12 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$); CSHARP False Replace argument null check with Guard clause - JsonApiDotNetCore.ArgumentGuard.NotNull($argument$, nameof($argument$)); + JsonApiDotNetCore.ArgumentGuard.NotNull($argument$); $left$ = $right$; $left$ = $right$ ?? throw new ArgumentNullException(nameof($argument$)); WARNING True - Replace classic argument null check with Guard clause + Replace argument == null check with Guard clause True True False @@ -615,7 +615,7 @@ $left$ = $right$; CSHARP False Replace argument null check with Guard clause - JsonApiDotNetCore.ArgumentGuard.NotNull($argument$, nameof($argument$)); + JsonApiDotNetCore.ArgumentGuard.NotNull($argument$); if ($argument$ == null) throw new ArgumentNullException(nameof($argument$)); WARNING True @@ -631,6 +631,19 @@ $left$ = $right$; $collection$.IsNullOrEmpty() $collection$ == null || !$collection$.Any() WARNING + True + Replace argument is null check with Guard clause + True + True + False + + IdentifierPlaceholder + True + CSHARP + False + JsonApiDotNetCore.ArgumentGuard.NotNull($argument$); + if ($argument$ is null) throw new ArgumentNullException(nameof($argument$)); + WARNING True True True diff --git a/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs b/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs index be336e56a0..e0c786a106 100644 --- a/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs +++ b/src/JsonApiDotNetCore.Annotations/ArgumentGuard.cs @@ -1,52 +1,40 @@ +using System.Runtime.CompilerServices; using JetBrains.Annotations; using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; #pragma warning disable AV1008 // Class should not be static +#pragma warning disable AV1553 // Do not use optional parameters with default value null for strings, collections or tasks namespace JsonApiDotNetCore; internal static class ArgumentGuard { [AssertionMethod] - public static void NotNull([NoEnumeration] [SysNotNull] T? value, [InvokerParameterName] string name) + public static void NotNull([NoEnumeration] [SysNotNull] T? value, [CallerArgumentExpression("value")] string? parameterName = null) where T : class { - if (value is null) - { - throw new ArgumentNullException(name); - } - } - - [AssertionMethod] - public static void NotNullNorEmpty([SysNotNull] IEnumerable? value, [InvokerParameterName] string name) - { - NotNull(value, name); - - if (!value.Any()) - { - throw new ArgumentException($"Must have one or more {name}.", name); - } + ArgumentNullException.ThrowIfNull(value, parameterName); } [AssertionMethod] - public static void NotNullNorEmpty([SysNotNull] IEnumerable? value, [InvokerParameterName] string name, string collectionName) + public static void NotNullNorEmpty([SysNotNull] IEnumerable? value, [CallerArgumentExpression("value")] string? parameterName = null) { - NotNull(value, name); + ArgumentNullException.ThrowIfNull(value, parameterName); if (!value.Any()) { - throw new ArgumentException($"Must have one or more {collectionName}.", name); + throw new ArgumentException("Collection cannot be null or empty.", parameterName); } } [AssertionMethod] - public static void NotNullNorEmpty([SysNotNull] string? value, [InvokerParameterName] string name) + public static void NotNullNorEmpty([SysNotNull] string? value, [CallerArgumentExpression("value")] string? parameterName = null) { - NotNull(value, name); + ArgumentNullException.ThrowIfNull(value, parameterName); if (value == string.Empty) { - throw new ArgumentException("String cannot be null or empty.", name); + throw new ArgumentException("String cannot be null or empty.", parameterName); } } } diff --git a/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs b/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs index a308607c3b..fa1c0c90bd 100644 --- a/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs +++ b/src/JsonApiDotNetCore.Annotations/CollectionConverter.cs @@ -26,8 +26,8 @@ internal sealed class CollectionConverter /// public IEnumerable CopyToTypedCollection(IEnumerable source, Type collectionType) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(collectionType, nameof(collectionType)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(collectionType); Type concreteCollectionType = ToConcreteCollectionType(collectionType); dynamic concreteCollectionInstance = Activator.CreateInstance(concreteCollectionType)!; @@ -121,7 +121,7 @@ public IReadOnlyCollection ExtractResources(object? value) /// public bool TypeCanContainHashSet(Type collectionType) { - ArgumentGuard.NotNull(collectionType, nameof(collectionType)); + ArgumentGuard.NotNull(collectionType); if (collectionType.IsGenericType) { diff --git a/src/JsonApiDotNetCore.Annotations/Configuration/ResourceType.cs b/src/JsonApiDotNetCore.Annotations/Configuration/ResourceType.cs index 515dfe8a63..0263958b00 100644 --- a/src/JsonApiDotNetCore.Annotations/Configuration/ResourceType.cs +++ b/src/JsonApiDotNetCore.Annotations/Configuration/ResourceType.cs @@ -100,9 +100,9 @@ public ResourceType(string publicName, Type clrType, Type identityClrType, IRead LinkTypes topLevelLinks = LinkTypes.NotConfigured, LinkTypes resourceLinks = LinkTypes.NotConfigured, LinkTypes relationshipLinks = LinkTypes.NotConfigured) { - ArgumentGuard.NotNullNorEmpty(publicName, nameof(publicName)); - ArgumentGuard.NotNull(clrType, nameof(clrType)); - ArgumentGuard.NotNull(identityClrType, nameof(identityClrType)); + ArgumentGuard.NotNullNorEmpty(publicName); + ArgumentGuard.NotNull(clrType); + ArgumentGuard.NotNull(identityClrType); PublicName = publicName; ClrType = clrType; @@ -153,7 +153,7 @@ public AttrAttribute GetAttributeByPublicName(string publicName) public AttrAttribute? FindAttributeByPublicName(string publicName) { - ArgumentGuard.NotNull(publicName, nameof(publicName)); + ArgumentGuard.NotNull(publicName); return _fieldsByPublicName.TryGetValue(publicName, out ResourceFieldAttribute? field) && field is AttrAttribute attribute ? attribute : null; } @@ -167,7 +167,7 @@ public AttrAttribute GetAttributeByPropertyName(string propertyName) public AttrAttribute? FindAttributeByPropertyName(string propertyName) { - ArgumentGuard.NotNull(propertyName, nameof(propertyName)); + ArgumentGuard.NotNull(propertyName); return _fieldsByPropertyName.TryGetValue(propertyName, out ResourceFieldAttribute? field) && field is AttrAttribute attribute ? attribute : null; } @@ -180,7 +180,7 @@ public RelationshipAttribute GetRelationshipByPublicName(string publicName) public RelationshipAttribute? FindRelationshipByPublicName(string publicName) { - ArgumentGuard.NotNull(publicName, nameof(publicName)); + ArgumentGuard.NotNull(publicName); return _fieldsByPublicName.TryGetValue(publicName, out ResourceFieldAttribute? field) && field is RelationshipAttribute relationship ? relationship @@ -197,7 +197,7 @@ public RelationshipAttribute GetRelationshipByPropertyName(string propertyName) public RelationshipAttribute? FindRelationshipByPropertyName(string propertyName) { - ArgumentGuard.NotNull(propertyName, nameof(propertyName)); + ArgumentGuard.NotNull(propertyName); return _fieldsByPropertyName.TryGetValue(propertyName, out ResourceFieldAttribute? field) && field is RelationshipAttribute relationship ? relationship @@ -217,7 +217,7 @@ public IReadOnlySet GetAllConcreteDerivedTypes() /// public ResourceType GetTypeOrDerived(Type clrType) { - ArgumentGuard.NotNull(clrType, nameof(clrType)); + ArgumentGuard.NotNull(clrType); ResourceType? derivedType = FindTypeOrDerived(this, clrType); diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs index 11320a7abc..c1b24e9567 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs @@ -57,7 +57,7 @@ public ResourceType RightType get => _rightType!; internal set { - ArgumentGuard.NotNull(value, nameof(value)); + ArgumentGuard.NotNull(value); _rightType = value; } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs index 599b17a42a..9f32610dc9 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs @@ -43,7 +43,7 @@ public PropertyInfo Property get => _property!; internal set { - ArgumentGuard.NotNull(value, nameof(value)); + ArgumentGuard.NotNull(value); _property = value; } } @@ -56,7 +56,7 @@ public ResourceType Type get => _type!; internal set { - ArgumentGuard.NotNull(value, nameof(value)); + ArgumentGuard.NotNull(value); _type = value; } } @@ -67,7 +67,7 @@ internal set /// public object? GetValue(object resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); if (Property.GetMethod == null) { @@ -92,7 +92,7 @@ internal set /// public void SetValue(object resource, object? newValue) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); if (Property.SetMethod == null) { diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Internal/RuntimeTypeConverter.cs b/src/JsonApiDotNetCore.Annotations/Resources/Internal/RuntimeTypeConverter.cs index 8722458938..d79ed4c635 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Internal/RuntimeTypeConverter.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Internal/RuntimeTypeConverter.cs @@ -10,7 +10,7 @@ public static class RuntimeTypeConverter { public static object? ConvertType(object? value, Type type) { - ArgumentGuard.NotNull(type, nameof(type)); + ArgumentGuard.NotNull(type); if (value == null) { diff --git a/src/JsonApiDotNetCore.Annotations/TypeExtensions.cs b/src/JsonApiDotNetCore.Annotations/TypeExtensions.cs index c28ea84332..b31f82d48e 100644 --- a/src/JsonApiDotNetCore.Annotations/TypeExtensions.cs +++ b/src/JsonApiDotNetCore.Annotations/TypeExtensions.cs @@ -15,7 +15,7 @@ public static bool IsOrImplementsInterface(this Type? source) /// private static bool IsOrImplementsInterface(this Type? source, Type interfaceType) { - ArgumentGuard.NotNull(interfaceType, nameof(interfaceType)); + ArgumentGuard.NotNull(interfaceType); if (source == null) { @@ -41,7 +41,7 @@ private static bool AreTypesEqual(Type left, Type right, bool isLeftGeneric) /// public static string GetFriendlyTypeName(this Type type) { - ArgumentGuard.NotNull(type, nameof(type)); + ArgumentGuard.NotNull(type); // Based on https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument. diff --git a/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransaction.cs b/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransaction.cs index be5125c414..f59f86162d 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransaction.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransaction.cs @@ -19,8 +19,8 @@ public sealed class EntityFrameworkCoreTransaction : IOperationsTransaction public EntityFrameworkCoreTransaction(IDbContextTransaction transaction, DbContext dbContext) { - ArgumentGuard.NotNull(transaction, nameof(transaction)); - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); + ArgumentGuard.NotNull(transaction); + ArgumentGuard.NotNull(dbContext); _transaction = transaction; _dbContext = dbContext; diff --git a/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransactionFactory.cs b/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransactionFactory.cs index 96c66e12ab..8ef44cb627 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransactionFactory.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/EntityFrameworkCoreTransactionFactory.cs @@ -15,8 +15,8 @@ public sealed class EntityFrameworkCoreTransactionFactory : IOperationsTransacti public EntityFrameworkCoreTransactionFactory(IDbContextResolver dbContextResolver, IJsonApiOptions options) { - ArgumentGuard.NotNull(dbContextResolver, nameof(dbContextResolver)); - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(dbContextResolver); + ArgumentGuard.NotNull(options); _dbContextResolver = dbContextResolver; _options = options; diff --git a/src/JsonApiDotNetCore/AtomicOperations/LocalIdTracker.cs b/src/JsonApiDotNetCore/AtomicOperations/LocalIdTracker.cs index b0a1b3ee2f..2def7bfecc 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/LocalIdTracker.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/LocalIdTracker.cs @@ -17,8 +17,8 @@ public void Reset() /// public void Declare(string localId, ResourceType resourceType) { - ArgumentGuard.NotNullNorEmpty(localId, nameof(localId)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNullNorEmpty(localId); + ArgumentGuard.NotNull(resourceType); AssertIsNotDeclared(localId); @@ -36,9 +36,9 @@ private void AssertIsNotDeclared(string localId) /// public void Assign(string localId, ResourceType resourceType, string stringId) { - ArgumentGuard.NotNullNorEmpty(localId, nameof(localId)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); - ArgumentGuard.NotNullNorEmpty(stringId, nameof(stringId)); + ArgumentGuard.NotNullNorEmpty(localId); + ArgumentGuard.NotNull(resourceType); + ArgumentGuard.NotNullNorEmpty(stringId); AssertIsDeclared(localId); @@ -57,8 +57,8 @@ public void Assign(string localId, ResourceType resourceType, string stringId) /// public string GetValue(string localId, ResourceType resourceType) { - ArgumentGuard.NotNullNorEmpty(localId, nameof(localId)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNullNorEmpty(localId); + ArgumentGuard.NotNull(resourceType); AssertIsDeclared(localId); diff --git a/src/JsonApiDotNetCore/AtomicOperations/LocalIdValidator.cs b/src/JsonApiDotNetCore/AtomicOperations/LocalIdValidator.cs index 9cb463ab10..fb75fe3c7f 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/LocalIdValidator.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/LocalIdValidator.cs @@ -18,8 +18,8 @@ public sealed class LocalIdValidator public LocalIdValidator(ILocalIdTracker localIdTracker, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(localIdTracker, nameof(localIdTracker)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(localIdTracker); + ArgumentGuard.NotNull(resourceGraph); _localIdTracker = localIdTracker; _resourceGraph = resourceGraph; @@ -27,7 +27,7 @@ public LocalIdValidator(ILocalIdTracker localIdTracker, IResourceGraph resourceG public void Validate(IEnumerable operations) { - ArgumentGuard.NotNull(operations, nameof(operations)); + ArgumentGuard.NotNull(operations); _localIdTracker.Reset(); diff --git a/src/JsonApiDotNetCore/AtomicOperations/OperationProcessorAccessor.cs b/src/JsonApiDotNetCore/AtomicOperations/OperationProcessorAccessor.cs index c032f78f8d..c2397267b3 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/OperationProcessorAccessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/OperationProcessorAccessor.cs @@ -15,7 +15,7 @@ public class OperationProcessorAccessor : IOperationProcessorAccessor public OperationProcessorAccessor(IServiceProvider serviceProvider) { - ArgumentGuard.NotNull(serviceProvider, nameof(serviceProvider)); + ArgumentGuard.NotNull(serviceProvider); _serviceProvider = serviceProvider; } @@ -23,7 +23,7 @@ public OperationProcessorAccessor(IServiceProvider serviceProvider) /// public Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); IOperationProcessor processor = ResolveProcessor(operation); return processor.ProcessAsync(operation, cancellationToken); diff --git a/src/JsonApiDotNetCore/AtomicOperations/OperationsProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/OperationsProcessor.cs index 6524252abf..6ecdfd6077 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/OperationsProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/OperationsProcessor.cs @@ -25,13 +25,13 @@ public OperationsProcessor(IOperationProcessorAccessor operationProcessorAccesso ILocalIdTracker localIdTracker, IResourceGraph resourceGraph, IJsonApiRequest request, ITargetedFields targetedFields, ISparseFieldSetCache sparseFieldSetCache) { - ArgumentGuard.NotNull(operationProcessorAccessor, nameof(operationProcessorAccessor)); - ArgumentGuard.NotNull(operationsTransactionFactory, nameof(operationsTransactionFactory)); - ArgumentGuard.NotNull(localIdTracker, nameof(localIdTracker)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); - ArgumentGuard.NotNull(sparseFieldSetCache, nameof(sparseFieldSetCache)); + ArgumentGuard.NotNull(operationProcessorAccessor); + ArgumentGuard.NotNull(operationsTransactionFactory); + ArgumentGuard.NotNull(localIdTracker); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(targetedFields); + ArgumentGuard.NotNull(sparseFieldSetCache); _operationProcessorAccessor = operationProcessorAccessor; _operationsTransactionFactory = operationsTransactionFactory; @@ -46,7 +46,7 @@ public OperationsProcessor(IOperationProcessorAccessor operationProcessorAccesso /// public virtual async Task> ProcessAsync(IList operations, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operations, nameof(operations)); + ArgumentGuard.NotNull(operations); _localIdValidator.Validate(operations); _localIdTracker.Reset(); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs index fc16847eec..c8997be8cd 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs @@ -13,7 +13,7 @@ public class AddToRelationshipProcessor : IAddToRelationshipProc public AddToRelationshipProcessor(IAddToRelationshipService service) { - ArgumentGuard.NotNull(service, nameof(service)); + ArgumentGuard.NotNull(service); _service = service; } @@ -21,7 +21,7 @@ public AddToRelationshipProcessor(IAddToRelationshipService serv /// public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); var leftId = (TId)operation.Resource.GetTypedId(); ISet rightResourceIds = operation.GetSecondaryResources(); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/CreateProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/CreateProcessor.cs index b06ebd626e..e105f54a50 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/CreateProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/CreateProcessor.cs @@ -14,8 +14,8 @@ public class CreateProcessor : ICreateProcessor public CreateProcessor(ICreateService service, ILocalIdTracker localIdTracker) { - ArgumentGuard.NotNull(service, nameof(service)); - ArgumentGuard.NotNull(localIdTracker, nameof(localIdTracker)); + ArgumentGuard.NotNull(service); + ArgumentGuard.NotNull(localIdTracker); _service = service; _localIdTracker = localIdTracker; @@ -24,7 +24,7 @@ public CreateProcessor(ICreateService service, ILocalIdTracker l /// public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); TResource? newResource = await _service.CreateAsync((TResource)operation.Resource, cancellationToken); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs index e4001b75c1..356742f9b7 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs @@ -13,7 +13,7 @@ public class DeleteProcessor : IDeleteProcessor public DeleteProcessor(IDeleteService service) { - ArgumentGuard.NotNull(service, nameof(service)); + ArgumentGuard.NotNull(service); _service = service; } @@ -21,7 +21,7 @@ public DeleteProcessor(IDeleteService service) /// public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); var id = (TId)operation.Resource.GetTypedId(); await _service.DeleteAsync(id, cancellationToken); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs index 493ed2066f..b308d6935a 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs @@ -13,7 +13,7 @@ public class RemoveFromRelationshipProcessor : IRemoveFromRelati public RemoveFromRelationshipProcessor(IRemoveFromRelationshipService service) { - ArgumentGuard.NotNull(service, nameof(service)); + ArgumentGuard.NotNull(service); _service = service; } @@ -21,7 +21,7 @@ public RemoveFromRelationshipProcessor(IRemoveFromRelationshipService public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); var leftId = (TId)operation.Resource.GetTypedId(); ISet rightResourceIds = operation.GetSecondaryResources(); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs index 5eb09ccbc3..083bd0d0fc 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs @@ -15,7 +15,7 @@ public class SetRelationshipProcessor : ISetRelationshipProcesso public SetRelationshipProcessor(ISetRelationshipService service) { - ArgumentGuard.NotNull(service, nameof(service)); + ArgumentGuard.NotNull(service); _service = service; } @@ -23,7 +23,7 @@ public SetRelationshipProcessor(ISetRelationshipService service) /// public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); var leftId = (TId)operation.Resource.GetTypedId(); object? rightValue = GetRelationshipRightValue(operation); diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs index a02ac2d3ff..5611f8d1c2 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs @@ -13,7 +13,7 @@ public class UpdateProcessor : IUpdateProcessor public UpdateProcessor(IUpdateService service) { - ArgumentGuard.NotNull(service, nameof(service)); + ArgumentGuard.NotNull(service); _service = service; } @@ -21,7 +21,7 @@ public UpdateProcessor(IUpdateService service) /// public virtual async Task ProcessAsync(OperationContainer operation, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); var resource = (TResource)operation.Resource; TResource? updated = await _service.UpdateAsync(resource.Id, resource, cancellationToken); diff --git a/src/JsonApiDotNetCore/AtomicOperations/RevertRequestStateOnDispose.cs b/src/JsonApiDotNetCore/AtomicOperations/RevertRequestStateOnDispose.cs index 453f78f1f2..1951333d0c 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/RevertRequestStateOnDispose.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/RevertRequestStateOnDispose.cs @@ -16,7 +16,7 @@ internal sealed class RevertRequestStateOnDispose : IDisposable public RevertRequestStateOnDispose(IJsonApiRequest request, ITargetedFields? targetedFields) { - ArgumentGuard.NotNull(request, nameof(request)); + ArgumentGuard.NotNull(request); _sourceRequest = request; _backupRequest.CopyFrom(request); diff --git a/src/JsonApiDotNetCore/CollectionExtensions.cs b/src/JsonApiDotNetCore/CollectionExtensions.cs index a7f5e72ab6..133231eb23 100644 --- a/src/JsonApiDotNetCore/CollectionExtensions.cs +++ b/src/JsonApiDotNetCore/CollectionExtensions.cs @@ -18,8 +18,8 @@ public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? so public static int FindIndex(this IReadOnlyList source, Predicate match) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(match, nameof(match)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(match); for (int index = 0; index < source.Count; index++) { @@ -82,8 +82,8 @@ public static IEnumerable WhereNotNull(this IEnumerable source) public static void AddRange(this ICollection source, IEnumerable itemsToAdd) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(itemsToAdd, nameof(itemsToAdd)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(itemsToAdd); foreach (T item in itemsToAdd) { diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index a941e27218..8ed4e42a42 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -22,7 +22,7 @@ public static class ApplicationBuilderExtensions /// public static void UseJsonApi(this IApplicationBuilder builder) { - ArgumentGuard.NotNull(builder, nameof(builder)); + ArgumentGuard.NotNull(builder); using (IServiceScope scope = builder.ApplicationServices.CreateScope()) { diff --git a/src/JsonApiDotNetCore/Configuration/InverseNavigationResolver.cs b/src/JsonApiDotNetCore/Configuration/InverseNavigationResolver.cs index 8e5f3f15a3..821be639f9 100644 --- a/src/JsonApiDotNetCore/Configuration/InverseNavigationResolver.cs +++ b/src/JsonApiDotNetCore/Configuration/InverseNavigationResolver.cs @@ -15,8 +15,8 @@ public sealed class InverseNavigationResolver : IInverseNavigationResolver public InverseNavigationResolver(IResourceGraph resourceGraph, IEnumerable dbContextResolvers) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(dbContextResolvers, nameof(dbContextResolvers)); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(dbContextResolvers); _resourceGraph = resourceGraph; _dbContextResolvers = dbContextResolvers; diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index 7cd4307ad1..82e0ff52e1 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -39,8 +39,8 @@ internal sealed class JsonApiApplicationBuilder : IJsonApiApplicationBuilder, ID public JsonApiApplicationBuilder(IServiceCollection services, IMvcCoreBuilder mvcBuilder) { - ArgumentGuard.NotNull(services, nameof(services)); - ArgumentGuard.NotNull(mvcBuilder, nameof(mvcBuilder)); + ArgumentGuard.NotNull(services); + ArgumentGuard.NotNull(mvcBuilder); _services = services; _mvcBuilder = mvcBuilder; @@ -73,7 +73,7 @@ public void ConfigureAutoDiscovery(Action? configureAuto /// public void ConfigureResourceGraph(ICollection dbContextTypes, Action? configureResourceGraph) { - ArgumentGuard.NotNull(dbContextTypes, nameof(dbContextTypes)); + ArgumentGuard.NotNull(dbContextTypes); _serviceDiscoveryFacade.DiscoverResources(); @@ -126,7 +126,7 @@ public void DiscoverInjectables() /// public void ConfigureServiceContainer(ICollection dbContextTypes) { - ArgumentGuard.NotNull(dbContextTypes, nameof(dbContextTypes)); + ArgumentGuard.NotNull(dbContextTypes); if (dbContextTypes.Any()) { diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs index 6b193bdc6f..a27acf8ebd 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs @@ -15,7 +15,7 @@ internal sealed class JsonApiValidationFilter : IPropertyValidationFilter public JsonApiValidationFilter(IHttpContextAccessor httpContextAccessor) { - ArgumentGuard.NotNull(httpContextAccessor, nameof(httpContextAccessor)); + ArgumentGuard.NotNull(httpContextAccessor); _httpContextAccessor = httpContextAccessor; } diff --git a/src/JsonApiDotNetCore/Configuration/ResourceDescriptor.cs b/src/JsonApiDotNetCore/Configuration/ResourceDescriptor.cs index 8747cdd18f..885f67567c 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceDescriptor.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceDescriptor.cs @@ -7,8 +7,8 @@ internal sealed class ResourceDescriptor public ResourceDescriptor(Type resourceClrType, Type idClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); - ArgumentGuard.NotNull(idClrType, nameof(idClrType)); + ArgumentGuard.NotNull(resourceClrType); + ArgumentGuard.NotNull(idClrType); ResourceClrType = resourceClrType; IdClrType = idClrType; diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraph.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraph.cs index d693fa2c3c..0dbaeb9623 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceGraph.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceGraph.cs @@ -18,7 +18,7 @@ public sealed class ResourceGraph : IResourceGraph public ResourceGraph(IReadOnlySet resourceTypeSet) { - ArgumentGuard.NotNull(resourceTypeSet, nameof(resourceTypeSet)); + ArgumentGuard.NotNull(resourceTypeSet); _resourceTypeSet = resourceTypeSet; @@ -51,7 +51,7 @@ public ResourceType GetResourceType(string publicName) /// public ResourceType? FindResourceType(string publicName) { - ArgumentGuard.NotNull(publicName, nameof(publicName)); + ArgumentGuard.NotNull(publicName); return _resourceTypesByPublicName.TryGetValue(publicName, out ResourceType? resourceType) ? resourceType : null; } @@ -72,7 +72,7 @@ public ResourceType GetResourceType(Type resourceClrType) /// public ResourceType? FindResourceType(Type resourceClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); Type typeToFind = IsLazyLoadingProxyForResourceType(resourceClrType) ? resourceClrType.BaseType! : resourceClrType; return _resourceTypesByClrType.TryGetValue(typeToFind, out ResourceType? resourceType) ? resourceType : null; @@ -94,7 +94,7 @@ public ResourceType GetResourceType() public IReadOnlyCollection GetFields(Expression> selector) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(selector, nameof(selector)); + ArgumentGuard.NotNull(selector); return FilterFields(selector); } @@ -103,7 +103,7 @@ public IReadOnlyCollection GetFields(Expressi public IReadOnlyCollection GetAttributes(Expression> selector) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(selector, nameof(selector)); + ArgumentGuard.NotNull(selector); return FilterFields(selector); } @@ -112,7 +112,7 @@ public IReadOnlyCollection GetAttributes(Expression GetRelationships(Expression> selector) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(selector, nameof(selector)); + ArgumentGuard.NotNull(selector); return FilterFields(selector); } diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs index 36eb9d37e8..2af0e63caf 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs @@ -22,8 +22,8 @@ public class ResourceGraphBuilder public ResourceGraphBuilder(IJsonApiOptions options, ILoggerFactory loggerFactory) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(loggerFactory); _options = options; _logger = loggerFactory.CreateLogger(); @@ -144,7 +144,7 @@ private static void ValidateRelationshipsInDerivedType(ResourceType resourceType public ResourceGraphBuilder Add(DbContext dbContext) { - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); + ArgumentGuard.NotNull(dbContext); foreach (IEntityType entityType in dbContext.Model.GetEntityTypes()) { @@ -200,7 +200,7 @@ public ResourceGraphBuilder Add(string? publicName = null) public ResourceGraphBuilder Add(Type resourceClrType, Type? idClrType = null, string? publicName = null) #pragma warning restore AV1553 // Do not use optional parameters with default value null for strings, collections or tasks { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); if (_resourceTypesByClrType.ContainsKey(resourceClrType)) { diff --git a/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs b/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs index 82a54ff010..a6e12951a9 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs @@ -19,7 +19,7 @@ public ResourceNameFormatter(JsonNamingPolicy? namingPolicy) /// public string FormatResourceName(Type resourceClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); var resourceAttribute = resourceClrType.GetCustomAttribute(true); diff --git a/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs index 8b5356fdef..7ea42a2470 100644 --- a/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs @@ -22,7 +22,7 @@ public static IServiceCollection AddJsonApi(this IServiceCollection services, Ac ICollection? dbContextTypes = null) #pragma warning restore AV1553 // Do not use optional parameters with default value null for strings, collections or tasks { - ArgumentGuard.NotNull(services, nameof(services)); + ArgumentGuard.NotNull(services); SetupApplicationBuilder(services, options, discovery, resources, mvcBuilder, dbContextTypes ?? Array.Empty()); @@ -59,7 +59,7 @@ private static void SetupApplicationBuilder(IServiceCollection services, Action< /// public static IServiceCollection AddResourceService(this IServiceCollection services) { - ArgumentGuard.NotNull(services, nameof(services)); + ArgumentGuard.NotNull(services); RegisterTypeForUnboundInterfaces(services, typeof(TService), ServiceDiscoveryFacade.ServiceUnboundInterfaces); @@ -72,7 +72,7 @@ public static IServiceCollection AddResourceService(this IServiceColle /// public static IServiceCollection AddResourceRepository(this IServiceCollection services) { - ArgumentGuard.NotNull(services, nameof(services)); + ArgumentGuard.NotNull(services); RegisterTypeForUnboundInterfaces(services, typeof(TRepository), ServiceDiscoveryFacade.RepositoryUnboundInterfaces); @@ -85,7 +85,7 @@ public static IServiceCollection AddResourceRepository(this IServic /// public static IServiceCollection AddResourceDefinition(this IServiceCollection services) { - ArgumentGuard.NotNull(services, nameof(services)); + ArgumentGuard.NotNull(services); RegisterTypeForUnboundInterfaces(services, typeof(TResourceDefinition), ServiceDiscoveryFacade.ResourceDefinitionUnboundInterfaces); diff --git a/src/JsonApiDotNetCore/Configuration/ServiceDiscoveryFacade.cs b/src/JsonApiDotNetCore/Configuration/ServiceDiscoveryFacade.cs index 7498391afd..85f95c232f 100644 --- a/src/JsonApiDotNetCore/Configuration/ServiceDiscoveryFacade.cs +++ b/src/JsonApiDotNetCore/Configuration/ServiceDiscoveryFacade.cs @@ -52,9 +52,9 @@ public sealed class ServiceDiscoveryFacade public ServiceDiscoveryFacade(IServiceCollection services, ResourceGraphBuilder resourceGraphBuilder, ILoggerFactory loggerFactory) { - ArgumentGuard.NotNull(services, nameof(services)); - ArgumentGuard.NotNull(resourceGraphBuilder, nameof(resourceGraphBuilder)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); + ArgumentGuard.NotNull(services); + ArgumentGuard.NotNull(resourceGraphBuilder); + ArgumentGuard.NotNull(loggerFactory); _logger = loggerFactory.CreateLogger(); _services = services; @@ -74,7 +74,7 @@ public ServiceDiscoveryFacade AddCurrentAssembly() /// public ServiceDiscoveryFacade AddAssembly(Assembly assembly) { - ArgumentGuard.NotNull(assembly, nameof(assembly)); + ArgumentGuard.NotNull(assembly); _assemblyCache.RegisterAssembly(assembly); _logger.LogDebug($"Registering assembly '{assembly.FullName}' for discovery of resources and injectables."); diff --git a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs index 2f004ffdf1..e4b1da8d01 100644 --- a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs +++ b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs @@ -66,9 +66,9 @@ internal sealed class TypeLocator public (Type implementationType, Type serviceInterface)? GetContainerRegistrationFromAssembly(Assembly assembly, Type unboundInterface, params Type[] interfaceTypeArguments) { - ArgumentGuard.NotNull(assembly, nameof(assembly)); - ArgumentGuard.NotNull(unboundInterface, nameof(unboundInterface)); - ArgumentGuard.NotNull(interfaceTypeArguments, nameof(interfaceTypeArguments)); + ArgumentGuard.NotNull(assembly); + ArgumentGuard.NotNull(unboundInterface); + ArgumentGuard.NotNull(interfaceTypeArguments); if (!unboundInterface.IsInterface || !unboundInterface.IsGenericType || unboundInterface != unboundInterface.GetGenericTypeDefinition()) { @@ -129,9 +129,9 @@ private static (Type implementationType, Type serviceInterface)? GetContainerReg /// public IReadOnlyCollection GetDerivedTypesForUnboundType(Assembly assembly, Type unboundType, params Type[] typeArguments) { - ArgumentGuard.NotNull(assembly, nameof(assembly)); - ArgumentGuard.NotNull(unboundType, nameof(unboundType)); - ArgumentGuard.NotNull(typeArguments, nameof(typeArguments)); + ArgumentGuard.NotNull(assembly); + ArgumentGuard.NotNull(unboundType); + ArgumentGuard.NotNull(typeArguments); Type closedType = unboundType.MakeGenericType(typeArguments); return GetDerivedTypes(assembly, closedType).ToArray(); @@ -153,8 +153,8 @@ public IReadOnlyCollection GetDerivedTypesForUnboundType(Assembly assembly /// public IEnumerable GetDerivedTypes(Assembly assembly, Type baseType) { - ArgumentGuard.NotNull(assembly, nameof(assembly)); - ArgumentGuard.NotNull(baseType, nameof(baseType)); + ArgumentGuard.NotNull(assembly); + ArgumentGuard.NotNull(baseType); foreach (Type type in assembly.GetTypes()) { diff --git a/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs b/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs index 975472ab28..eba4b8340c 100644 --- a/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs +++ b/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs @@ -46,7 +46,7 @@ public DisableQueryStringAttribute(JsonApiQueryStringParameters parameters) /// public DisableQueryStringAttribute(string parameterNames) { - ArgumentGuard.NotNullNorEmpty(parameterNames, nameof(parameterNames)); + ArgumentGuard.NotNullNorEmpty(parameterNames); ParameterNames = parameterNames.Split(",").ToHashSet(); } diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index cabe4d49d8..22efab2840 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -63,9 +63,9 @@ protected BaseJsonApiController(IJsonApiOptions options, IResourceGraph resource IUpdateService? update = null, ISetRelationshipService? setRelationship = null, IDeleteService? delete = null, IRemoveFromRelationshipService? removeFromRelationship = null) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(loggerFactory); _options = options; _resourceGraph = resourceGraph; @@ -139,7 +139,7 @@ public virtual async Task GetSecondaryAsync(TId id, string relati relationshipName }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); + ArgumentGuard.NotNullNorEmpty(relationshipName); if (_getSecondary == null) { @@ -168,7 +168,7 @@ public virtual async Task GetRelationshipAsync(TId id, string rel relationshipName }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); + ArgumentGuard.NotNullNorEmpty(relationshipName); if (_getRelationship == null) { @@ -192,7 +192,7 @@ public virtual async Task PostAsync([FromBody] TResource resource resource }); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); if (_create == null) { @@ -245,8 +245,8 @@ public virtual async Task PostRelationshipAsync(TId id, string re rightResourceIds }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNullNorEmpty(relationshipName); + ArgumentGuard.NotNull(rightResourceIds); if (_addToRelationship == null) { @@ -272,7 +272,7 @@ public virtual async Task PatchAsync(TId id, [FromBody] TResource resource }); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); if (_update == null) { @@ -320,7 +320,7 @@ public virtual async Task PatchRelationshipAsync(TId id, string r rightValue }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); + ArgumentGuard.NotNullNorEmpty(relationshipName); if (_setRelationship == null) { @@ -381,8 +381,8 @@ public virtual async Task DeleteRelationshipAsync(TId id, string rightResourceIds }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNullNorEmpty(relationshipName); + ArgumentGuard.NotNull(rightResourceIds); if (_removeFromRelationship == null) { diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs index a948d67edc..2ea8f89a87 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiOperationsController.cs @@ -27,12 +27,12 @@ public abstract class BaseJsonApiOperationsController : CoreJsonApiController protected BaseJsonApiOperationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, ITargetedFields targetedFields) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); - ArgumentGuard.NotNull(processor, nameof(processor)); - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(loggerFactory); + ArgumentGuard.NotNull(processor); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(targetedFields); _options = options; _resourceGraph = resourceGraph; @@ -109,7 +109,7 @@ public virtual async Task PostOperationsAsync([FromBody] IList errors) { IReadOnlyList? errorList = ToErrorList(errors); - ArgumentGuard.NotNullNorEmpty(errorList, nameof(errors)); + ArgumentGuard.NotNullNorEmpty(errorList); return new ObjectResult(errorList) { diff --git a/src/JsonApiDotNetCore/Diagnostics/AspNetCodeTimerSession.cs b/src/JsonApiDotNetCore/Diagnostics/AspNetCodeTimerSession.cs index bef29dba78..a42d734580 100644 --- a/src/JsonApiDotNetCore/Diagnostics/AspNetCodeTimerSession.cs +++ b/src/JsonApiDotNetCore/Diagnostics/AspNetCodeTimerSession.cs @@ -37,14 +37,14 @@ public ICodeTimer CodeTimer public AspNetCodeTimerSession(IHttpContextAccessor httpContextAccessor) { - ArgumentGuard.NotNull(httpContextAccessor, nameof(httpContextAccessor)); + ArgumentGuard.NotNull(httpContextAccessor); _httpContextAccessor = httpContextAccessor; } public AspNetCodeTimerSession(HttpContext httpContext) { - ArgumentGuard.NotNull(httpContext, nameof(httpContext)); + ArgumentGuard.NotNull(httpContext); _httpContext = httpContext; } diff --git a/src/JsonApiDotNetCore/Diagnostics/CodeTimingSessionManager.cs b/src/JsonApiDotNetCore/Diagnostics/CodeTimingSessionManager.cs index d858aa6f4b..5a862409bc 100644 --- a/src/JsonApiDotNetCore/Diagnostics/CodeTimingSessionManager.cs +++ b/src/JsonApiDotNetCore/Diagnostics/CodeTimingSessionManager.cs @@ -62,7 +62,7 @@ private static void AssertHasActiveSession() public static void Capture(ICodeTimerSession session) { - ArgumentGuard.NotNull(session, nameof(session)); + ArgumentGuard.NotNull(session); AssertNoActiveSession(); diff --git a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs index eede4fed25..be4de87fc8 100644 --- a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs +++ b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs @@ -26,9 +26,9 @@ public InvalidModelStateException(IReadOnlyDictionary private static IEnumerable FromModelStateDictionary(IReadOnlyDictionary modelState, Type modelType, IResourceGraph resourceGraph, bool includeExceptionStackTraceInErrors, Func? getCollectionElementTypeCallback) { - ArgumentGuard.NotNull(modelState, nameof(modelState)); - ArgumentGuard.NotNull(modelType, nameof(modelType)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(modelState); + ArgumentGuard.NotNull(modelType); + ArgumentGuard.NotNull(resourceGraph); List errorObjects = new(); @@ -229,8 +229,8 @@ private abstract class ModelStateKeySegment protected ModelStateKeySegment(Type modelType, bool isInComplexType, string nextKey, string? sourcePointer, ModelStateKeySegment? parent, Func? getCollectionElementTypeCallback) { - ArgumentGuard.NotNull(modelType, nameof(modelType)); - ArgumentGuard.NotNull(nextKey, nameof(nextKey)); + ArgumentGuard.NotNull(modelType); + ArgumentGuard.NotNull(nextKey); ModelType = modelType; IsInComplexType = isInComplexType; @@ -242,15 +242,15 @@ protected ModelStateKeySegment(Type modelType, bool isInComplexType, string next public ModelStateKeySegment? GetNextSegment(Type modelType, bool isInComplexType, string? sourcePointer) { - ArgumentGuard.NotNull(modelType, nameof(modelType)); + ArgumentGuard.NotNull(modelType); return _nextKey == string.Empty ? null : CreateSegment(modelType, _nextKey, isInComplexType, this, sourcePointer, GetCollectionElementTypeCallback); } public static ModelStateKeySegment Create(Type modelType, string key, Func? getCollectionElementTypeCallback) { - ArgumentGuard.NotNull(modelType, nameof(modelType)); - ArgumentGuard.NotNull(key, nameof(key)); + ArgumentGuard.NotNull(modelType); + ArgumentGuard.NotNull(key); return CreateSegment(modelType, key, false, null, null, getCollectionElementTypeCallback); } @@ -359,14 +359,14 @@ public PropertySegment(string propertyName, Type modelType, bool isInComplexType Func? getCollectionElementTypeCallback) : base(modelType, isInComplexType, nextKey, sourcePointer, parent, getCollectionElementTypeCallback) { - ArgumentGuard.NotNull(propertyName, nameof(propertyName)); + ArgumentGuard.NotNull(propertyName); PropertyName = propertyName; } public static string GetPublicNameForProperty(PropertyInfo property) { - ArgumentGuard.NotNull(property, nameof(property)); + ArgumentGuard.NotNull(property); var jsonNameAttribute = property.GetCustomAttribute(true); return jsonNameAttribute?.Name ?? property.Name; diff --git a/src/JsonApiDotNetCore/Errors/JsonApiException.cs b/src/JsonApiDotNetCore/Errors/JsonApiException.cs index 6bb62177dc..4571843e8d 100644 --- a/src/JsonApiDotNetCore/Errors/JsonApiException.cs +++ b/src/JsonApiDotNetCore/Errors/JsonApiException.cs @@ -24,7 +24,7 @@ public class JsonApiException : Exception public JsonApiException(ErrorObject error, Exception? innerException = null) : base(null, innerException) { - ArgumentGuard.NotNull(error, nameof(error)); + ArgumentGuard.NotNull(error); Errors = error.AsArray(); } @@ -33,7 +33,7 @@ public JsonApiException(IEnumerable errors, Exception? innerExcepti : base(null, innerException) { IReadOnlyList? errorList = ToErrorList(errors); - ArgumentGuard.NotNullNorEmpty(errorList, nameof(errors)); + ArgumentGuard.NotNullNorEmpty(errorList); Errors = errorList; } diff --git a/src/JsonApiDotNetCore/Errors/MissingResourceInRelationship.cs b/src/JsonApiDotNetCore/Errors/MissingResourceInRelationship.cs index e80181898d..42082d6126 100644 --- a/src/JsonApiDotNetCore/Errors/MissingResourceInRelationship.cs +++ b/src/JsonApiDotNetCore/Errors/MissingResourceInRelationship.cs @@ -11,9 +11,9 @@ public sealed class MissingResourceInRelationship public MissingResourceInRelationship(string relationshipName, string resourceType, string resourceId) { - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); - ArgumentGuard.NotNullNorEmpty(resourceType, nameof(resourceType)); - ArgumentGuard.NotNullNorEmpty(resourceId, nameof(resourceId)); + ArgumentGuard.NotNullNorEmpty(relationshipName); + ArgumentGuard.NotNullNorEmpty(resourceType); + ArgumentGuard.NotNullNorEmpty(resourceId); RelationshipName = relationshipName; ResourceType = resourceType; diff --git a/src/JsonApiDotNetCore/Errors/UnsuccessfulActionResultException.cs b/src/JsonApiDotNetCore/Errors/UnsuccessfulActionResultException.cs index c5c55e4b70..e739ec9cbf 100644 --- a/src/JsonApiDotNetCore/Errors/UnsuccessfulActionResultException.cs +++ b/src/JsonApiDotNetCore/Errors/UnsuccessfulActionResultException.cs @@ -26,7 +26,7 @@ public UnsuccessfulActionResultException(ProblemDetails problemDetails) private static ErrorObject ToError(ProblemDetails problemDetails) { - ArgumentGuard.NotNull(problemDetails, nameof(problemDetails)); + ArgumentGuard.NotNull(problemDetails); HttpStatusCode status = problemDetails.Status != null ? (HttpStatusCode)problemDetails.Status.Value : HttpStatusCode.InternalServerError; diff --git a/src/JsonApiDotNetCore/Middleware/AsyncConvertEmptyActionResultFilter.cs b/src/JsonApiDotNetCore/Middleware/AsyncConvertEmptyActionResultFilter.cs index cf51a82733..82e443a9af 100644 --- a/src/JsonApiDotNetCore/Middleware/AsyncConvertEmptyActionResultFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/AsyncConvertEmptyActionResultFilter.cs @@ -10,8 +10,8 @@ public sealed class AsyncConvertEmptyActionResultFilter : IAsyncConvertEmptyActi /// public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { - ArgumentGuard.NotNull(context, nameof(context)); - ArgumentGuard.NotNull(next, nameof(next)); + ArgumentGuard.NotNull(context); + ArgumentGuard.NotNull(next); if (context.HttpContext.IsJsonApiRequest()) { diff --git a/src/JsonApiDotNetCore/Middleware/AsyncJsonApiExceptionFilter.cs b/src/JsonApiDotNetCore/Middleware/AsyncJsonApiExceptionFilter.cs index e87fc98389..58d568a3ec 100644 --- a/src/JsonApiDotNetCore/Middleware/AsyncJsonApiExceptionFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/AsyncJsonApiExceptionFilter.cs @@ -13,7 +13,7 @@ public sealed class AsyncJsonApiExceptionFilter : IAsyncJsonApiExceptionFilter public AsyncJsonApiExceptionFilter(IExceptionHandler exceptionHandler) { - ArgumentGuard.NotNull(exceptionHandler, nameof(exceptionHandler)); + ArgumentGuard.NotNull(exceptionHandler); _exceptionHandler = exceptionHandler; } @@ -21,7 +21,7 @@ public AsyncJsonApiExceptionFilter(IExceptionHandler exceptionHandler) /// public Task OnExceptionAsync(ExceptionContext context) { - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(context); if (context.HttpContext.IsJsonApiRequest()) { diff --git a/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs b/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs index 6f31c28d2a..89164c844f 100644 --- a/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs +++ b/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs @@ -12,7 +12,7 @@ public sealed class AsyncQueryStringActionFilter : IAsyncQueryStringActionFilter public AsyncQueryStringActionFilter(IQueryStringReader queryStringReader) { - ArgumentGuard.NotNull(queryStringReader, nameof(queryStringReader)); + ArgumentGuard.NotNull(queryStringReader); _queryStringReader = queryStringReader; } @@ -20,8 +20,8 @@ public AsyncQueryStringActionFilter(IQueryStringReader queryStringReader) /// public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { - ArgumentGuard.NotNull(context, nameof(context)); - ArgumentGuard.NotNull(next, nameof(next)); + ArgumentGuard.NotNull(context); + ArgumentGuard.NotNull(next); if (context.HttpContext.IsJsonApiRequest()) { diff --git a/src/JsonApiDotNetCore/Middleware/ExceptionHandler.cs b/src/JsonApiDotNetCore/Middleware/ExceptionHandler.cs index ea1d67743d..b8690402a5 100644 --- a/src/JsonApiDotNetCore/Middleware/ExceptionHandler.cs +++ b/src/JsonApiDotNetCore/Middleware/ExceptionHandler.cs @@ -17,8 +17,8 @@ public class ExceptionHandler : IExceptionHandler public ExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options) { - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(loggerFactory); + ArgumentGuard.NotNull(options); _options = options; _logger = loggerFactory.CreateLogger(); @@ -26,7 +26,7 @@ public ExceptionHandler(ILoggerFactory loggerFactory, IJsonApiOptions options) public IReadOnlyList HandleException(Exception exception) { - ArgumentGuard.NotNull(exception, nameof(exception)); + ArgumentGuard.NotNull(exception); Exception demystified = exception.Demystify(); @@ -45,7 +45,7 @@ private void LogException(Exception exception) protected virtual LogLevel GetLogLevel(Exception exception) { - ArgumentGuard.NotNull(exception, nameof(exception)); + ArgumentGuard.NotNull(exception); if (exception is OperationCanceledException) { @@ -62,14 +62,14 @@ protected virtual LogLevel GetLogLevel(Exception exception) protected virtual string GetLogMessage(Exception exception) { - ArgumentGuard.NotNull(exception, nameof(exception)); + ArgumentGuard.NotNull(exception); return exception is JsonApiException jsonApiException ? jsonApiException.GetSummary() : exception.Message; } protected virtual IReadOnlyList CreateErrorResponse(Exception exception) { - ArgumentGuard.NotNull(exception, nameof(exception)); + ArgumentGuard.NotNull(exception); IReadOnlyList errors = exception is JsonApiException jsonApiException ? jsonApiException.Errors : exception is OperationCanceledException ? new ErrorObject((HttpStatusCode)499) diff --git a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs index b6785e8198..a675aeeaff 100644 --- a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs +++ b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs @@ -13,7 +13,7 @@ public static class HttpContextExtensions /// public static bool IsJsonApiRequest(this HttpContext httpContext) { - ArgumentGuard.NotNull(httpContext, nameof(httpContext)); + ArgumentGuard.NotNull(httpContext); string? value = httpContext.Items[IsJsonApiRequestKey] as string; return value == bool.TrueString; @@ -21,7 +21,7 @@ public static bool IsJsonApiRequest(this HttpContext httpContext) internal static void RegisterJsonApiRequest(this HttpContext httpContext) { - ArgumentGuard.NotNull(httpContext, nameof(httpContext)); + ArgumentGuard.NotNull(httpContext); httpContext.Items[IsJsonApiRequestKey] = bool.TrueString; } diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs b/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs index 077f0573f0..59563c9268 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs @@ -10,7 +10,7 @@ public sealed class JsonApiInputFormatter : IJsonApiInputFormatter /// public bool CanRead(InputFormatterContext context) { - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(context); return context.HttpContext.IsJsonApiRequest(); } @@ -18,7 +18,7 @@ public bool CanRead(InputFormatterContext context) /// public async Task ReadAsync(InputFormatterContext context) { - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(context); var reader = context.HttpContext.RequestServices.GetRequiredService(); diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs b/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs index 2e15e6ae9a..b38ad986dd 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs @@ -36,11 +36,11 @@ public JsonApiMiddleware(RequestDelegate next, IHttpContextAccessor httpContextA public async Task InvokeAsync(HttpContext httpContext, IControllerResourceMapping controllerResourceMapping, IJsonApiOptions options, IJsonApiRequest request, ILogger logger) { - ArgumentGuard.NotNull(httpContext, nameof(httpContext)); - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(logger, nameof(logger)); + ArgumentGuard.NotNull(httpContext); + ArgumentGuard.NotNull(controllerResourceMapping); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(logger); using (CodeTimingSessionManager.Current.Measure("JSON:API middleware")) { diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs b/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs index c32bb9d9f9..8c97a12ea4 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs @@ -10,7 +10,7 @@ public sealed class JsonApiOutputFormatter : IJsonApiOutputFormatter /// public bool CanWriteResult(OutputFormatterCanWriteContext context) { - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(context); return context.HttpContext.IsJsonApiRequest(); } @@ -18,7 +18,7 @@ public bool CanWriteResult(OutputFormatterCanWriteContext context) /// public async Task WriteAsync(OutputFormatterWriteContext context) { - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(context); var writer = context.HttpContext.RequestServices.GetRequiredService(); await writer.WriteAsync(context.Object, context.HttpContext); diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiRequest.cs b/src/JsonApiDotNetCore/Middleware/JsonApiRequest.cs index a28c01fcd6..98e42823a3 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiRequest.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiRequest.cs @@ -38,7 +38,7 @@ public sealed class JsonApiRequest : IJsonApiRequest /// public void CopyFrom(IJsonApiRequest other) { - ArgumentGuard.NotNull(other, nameof(other)); + ArgumentGuard.NotNull(other); Kind = other.Kind; PrimaryId = other.PrimaryId; diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs index fe95d93446..a05aa979ef 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs @@ -36,8 +36,8 @@ public sealed class JsonApiRoutingConvention : IJsonApiRoutingConvention public JsonApiRoutingConvention(IJsonApiOptions options, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(resourceGraph); _options = options; _resourceGraph = resourceGraph; @@ -60,7 +60,7 @@ public JsonApiRoutingConvention(IJsonApiOptions options, IResourceGraph resource /// public void Apply(ApplicationModel application) { - ArgumentGuard.NotNull(application, nameof(application)); + ArgumentGuard.NotNull(application); foreach (ControllerModel controller in application.Controllers) { diff --git a/src/JsonApiDotNetCore/Queries/ExpressionInScope.cs b/src/JsonApiDotNetCore/Queries/ExpressionInScope.cs index 37d40f127b..6ac6f75059 100644 --- a/src/JsonApiDotNetCore/Queries/ExpressionInScope.cs +++ b/src/JsonApiDotNetCore/Queries/ExpressionInScope.cs @@ -15,7 +15,7 @@ public class ExpressionInScope public ExpressionInScope(ResourceFieldChainExpression? scope, QueryExpression expression) { - ArgumentGuard.NotNull(expression, nameof(expression)); + ArgumentGuard.NotNull(expression); Scope = scope; Expression = expression; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs index 980a7846bc..2b855b1bdb 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/AnyExpression.cs @@ -16,8 +16,8 @@ public class AnyExpression : FilterExpression public AnyExpression(ResourceFieldChainExpression targetAttribute, IImmutableSet constants) { - ArgumentGuard.NotNull(targetAttribute, nameof(targetAttribute)); - ArgumentGuard.NotNull(constants, nameof(constants)); + ArgumentGuard.NotNull(targetAttribute); + ArgumentGuard.NotNull(constants); if (constants.Count < 2) { diff --git a/src/JsonApiDotNetCore/Queries/Expressions/ComparisonExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/ComparisonExpression.cs index 9bf1c3bde8..cdae713f3d 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/ComparisonExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/ComparisonExpression.cs @@ -15,8 +15,8 @@ public class ComparisonExpression : FilterExpression public ComparisonExpression(ComparisonOperator @operator, QueryExpression left, QueryExpression right) { - ArgumentGuard.NotNull(left, nameof(left)); - ArgumentGuard.NotNull(right, nameof(right)); + ArgumentGuard.NotNull(left); + ArgumentGuard.NotNull(right); Operator = @operator; Left = left; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/CountExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/CountExpression.cs index 5de89ead7c..2eff0a86e9 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/CountExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/CountExpression.cs @@ -13,7 +13,7 @@ public class CountExpression : FunctionExpression public CountExpression(ResourceFieldChainExpression targetCollection) { - ArgumentGuard.NotNull(targetCollection, nameof(targetCollection)); + ArgumentGuard.NotNull(targetCollection); TargetCollection = targetCollection; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/HasExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/HasExpression.cs index c5387106d6..825119fe33 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/HasExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/HasExpression.cs @@ -15,7 +15,7 @@ public class HasExpression : FilterExpression public HasExpression(ResourceFieldChainExpression targetCollection, FilterExpression? filter) { - ArgumentGuard.NotNull(targetCollection, nameof(targetCollection)); + ArgumentGuard.NotNull(targetCollection); TargetCollection = targetCollection; Filter = filter; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/IncludeChainConverter.cs b/src/JsonApiDotNetCore/Queries/Expressions/IncludeChainConverter.cs index b35c48efbd..8b2034a374 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/IncludeChainConverter.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/IncludeChainConverter.cs @@ -29,7 +29,7 @@ internal sealed class IncludeChainConverter /// public IReadOnlyCollection GetRelationshipChains(IncludeExpression include) { - ArgumentGuard.NotNull(include, nameof(include)); + ArgumentGuard.NotNull(include); if (!include.Elements.Any()) { diff --git a/src/JsonApiDotNetCore/Queries/Expressions/IncludeElementExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/IncludeElementExpression.cs index e76aaf0946..01c25dad4e 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/IncludeElementExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/IncludeElementExpression.cs @@ -21,8 +21,8 @@ public IncludeElementExpression(RelationshipAttribute relationship) public IncludeElementExpression(RelationshipAttribute relationship, IImmutableSet children) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); - ArgumentGuard.NotNull(children, nameof(children)); + ArgumentGuard.NotNull(relationship); + ArgumentGuard.NotNull(children); Relationship = relationship; Children = children; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/IncludeExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/IncludeExpression.cs index a63d87719d..69373c9abf 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/IncludeExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/IncludeExpression.cs @@ -17,7 +17,7 @@ public class IncludeExpression : QueryExpression public IncludeExpression(IImmutableSet elements) { - ArgumentGuard.NotNullNorEmpty(elements, nameof(elements)); + ArgumentGuard.NotNullNorEmpty(elements); Elements = elements; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/IsTypeExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/IsTypeExpression.cs index a30e31308b..4e259b358e 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/IsTypeExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/IsTypeExpression.cs @@ -18,7 +18,7 @@ public class IsTypeExpression : FilterExpression public IsTypeExpression(ResourceFieldChainExpression? targetToOneRelationship, ResourceType derivedType, FilterExpression? child) { - ArgumentGuard.NotNull(derivedType, nameof(derivedType)); + ArgumentGuard.NotNull(derivedType); TargetToOneRelationship = targetToOneRelationship; DerivedType = derivedType; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/LiteralConstantExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/LiteralConstantExpression.cs index 17c62f230f..578643d5db 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/LiteralConstantExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/LiteralConstantExpression.cs @@ -12,7 +12,7 @@ public class LiteralConstantExpression : IdentifierExpression public LiteralConstantExpression(string text) { - ArgumentGuard.NotNull(text, nameof(text)); + ArgumentGuard.NotNull(text); Value = text; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs index c8d8ffb24b..08f970aee5 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/LogicalExpression.cs @@ -21,7 +21,7 @@ public LogicalExpression(LogicalOperator @operator, params FilterExpression[] te public LogicalExpression(LogicalOperator @operator, IImmutableList terms) { - ArgumentGuard.NotNull(terms, nameof(terms)); + ArgumentGuard.NotNull(terms); if (terms.Count < 2) { @@ -34,7 +34,7 @@ public LogicalExpression(LogicalOperator @operator, IImmutableList terms = filters.WhereNotNull().ToImmutableArray(); diff --git a/src/JsonApiDotNetCore/Queries/Expressions/MatchTextExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/MatchTextExpression.cs index a9c598402b..5d9ed08859 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/MatchTextExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/MatchTextExpression.cs @@ -16,8 +16,8 @@ public class MatchTextExpression : FilterExpression public MatchTextExpression(ResourceFieldChainExpression targetAttribute, LiteralConstantExpression textValue, TextMatchKind matchKind) { - ArgumentGuard.NotNull(targetAttribute, nameof(targetAttribute)); - ArgumentGuard.NotNull(textValue, nameof(textValue)); + ArgumentGuard.NotNull(targetAttribute); + ArgumentGuard.NotNull(textValue); TargetAttribute = targetAttribute; TextValue = textValue; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/NotExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/NotExpression.cs index 4d28c4a9c3..ae198cd3ee 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/NotExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/NotExpression.cs @@ -13,7 +13,7 @@ public class NotExpression : FilterExpression public NotExpression(FilterExpression child) { - ArgumentGuard.NotNull(child, nameof(child)); + ArgumentGuard.NotNull(child); Child = child; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/PaginationExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/PaginationExpression.cs index 97ff8b1456..2ecd9901a2 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/PaginationExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/PaginationExpression.cs @@ -14,7 +14,7 @@ public class PaginationExpression : QueryExpression public PaginationExpression(PageNumber pageNumber, PageSize? pageSize) { - ArgumentGuard.NotNull(pageNumber, nameof(pageNumber)); + ArgumentGuard.NotNull(pageNumber); PageNumber = pageNumber; PageSize = pageSize; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/PaginationQueryStringValueExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/PaginationQueryStringValueExpression.cs index 594dab297a..a65e9c0a15 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/PaginationQueryStringValueExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/PaginationQueryStringValueExpression.cs @@ -13,7 +13,7 @@ public class PaginationQueryStringValueExpression : QueryExpression public PaginationQueryStringValueExpression(IImmutableList elements) { - ArgumentGuard.NotNullNorEmpty(elements, nameof(elements)); + ArgumentGuard.NotNullNorEmpty(elements); Elements = elements; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/QueryStringParameterScopeExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/QueryStringParameterScopeExpression.cs index e567da8778..bc2d018033 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/QueryStringParameterScopeExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/QueryStringParameterScopeExpression.cs @@ -13,7 +13,7 @@ public class QueryStringParameterScopeExpression : QueryExpression public QueryStringParameterScopeExpression(LiteralConstantExpression parameterName, ResourceFieldChainExpression? scope) { - ArgumentGuard.NotNull(parameterName, nameof(parameterName)); + ArgumentGuard.NotNull(parameterName); ParameterName = parameterName; Scope = scope; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/QueryableHandlerExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/QueryableHandlerExpression.cs index 1d9c910955..872cdb1aac 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/QueryableHandlerExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/QueryableHandlerExpression.cs @@ -15,7 +15,7 @@ public class QueryableHandlerExpression : QueryExpression public QueryableHandlerExpression(object queryableHandler, StringValues parameterValue) { - ArgumentGuard.NotNull(queryableHandler, nameof(queryableHandler)); + ArgumentGuard.NotNull(queryableHandler); _queryableHandler = queryableHandler; _parameterValue = parameterValue; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/ResourceFieldChainExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/ResourceFieldChainExpression.cs index 7decec6221..9224642133 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/ResourceFieldChainExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/ResourceFieldChainExpression.cs @@ -14,14 +14,14 @@ public class ResourceFieldChainExpression : IdentifierExpression public ResourceFieldChainExpression(ResourceFieldAttribute field) { - ArgumentGuard.NotNull(field, nameof(field)); + ArgumentGuard.NotNull(field); Fields = ImmutableArray.Create(field); } public ResourceFieldChainExpression(IImmutableList fields) { - ArgumentGuard.NotNullNorEmpty(fields, nameof(fields)); + ArgumentGuard.NotNullNorEmpty(fields); Fields = fields; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/SortElementExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/SortElementExpression.cs index 78de440a42..bfdf30e8d5 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/SortElementExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/SortElementExpression.cs @@ -15,7 +15,7 @@ public class SortElementExpression : QueryExpression public SortElementExpression(ResourceFieldChainExpression targetAttribute, bool isAscending) { - ArgumentGuard.NotNull(targetAttribute, nameof(targetAttribute)); + ArgumentGuard.NotNull(targetAttribute); TargetAttribute = targetAttribute; IsAscending = isAscending; @@ -23,7 +23,7 @@ public SortElementExpression(ResourceFieldChainExpression targetAttribute, bool public SortElementExpression(CountExpression count, bool isAscending) { - ArgumentGuard.NotNull(count, nameof(count)); + ArgumentGuard.NotNull(count); Count = count; IsAscending = isAscending; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/SortExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/SortExpression.cs index dc0aebd320..53b067d4e8 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/SortExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/SortExpression.cs @@ -13,7 +13,7 @@ public class SortExpression : QueryExpression public SortExpression(IImmutableList elements) { - ArgumentGuard.NotNullNorEmpty(elements, nameof(elements)); + ArgumentGuard.NotNullNorEmpty(elements); Elements = elements; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpression.cs index bc1e611bd8..f36427b2e1 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpression.cs @@ -14,7 +14,7 @@ public class SparseFieldSetExpression : QueryExpression public SparseFieldSetExpression(IImmutableSet fields) { - ArgumentGuard.NotNullNorEmpty(fields, nameof(fields)); + ArgumentGuard.NotNullNorEmpty(fields); Fields = fields; } diff --git a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpressionExtensions.cs b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpressionExtensions.cs index 53f9ff0eb6..c7c331eb46 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpressionExtensions.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldSetExpressionExtensions.cs @@ -14,8 +14,8 @@ public static class SparseFieldSetExpressionExtensions Expression> fieldSelector, IResourceGraph resourceGraph) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(fieldSelector, nameof(fieldSelector)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(fieldSelector); + ArgumentGuard.NotNull(resourceGraph); SparseFieldSetExpression? newSparseFieldSet = sparseFieldSet; @@ -42,8 +42,8 @@ public static class SparseFieldSetExpressionExtensions Expression> fieldSelector, IResourceGraph resourceGraph) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(fieldSelector, nameof(fieldSelector)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(fieldSelector); + ArgumentGuard.NotNull(resourceGraph); SparseFieldSetExpression? newSparseFieldSet = sparseFieldSet; diff --git a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldTableExpression.cs b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldTableExpression.cs index 8e52df9b3b..c69be71292 100644 --- a/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldTableExpression.cs +++ b/src/JsonApiDotNetCore/Queries/Expressions/SparseFieldTableExpression.cs @@ -15,7 +15,7 @@ public class SparseFieldTableExpression : QueryExpression public SparseFieldTableExpression(IImmutableDictionary table) { - ArgumentGuard.NotNullNorEmpty(table, nameof(table), "entries"); + ArgumentGuard.NotNullNorEmpty(table); Table = table; } diff --git a/src/JsonApiDotNetCore/Queries/FieldSelection.cs b/src/JsonApiDotNetCore/Queries/FieldSelection.cs index 54c59005bf..7f62db1fcf 100644 --- a/src/JsonApiDotNetCore/Queries/FieldSelection.cs +++ b/src/JsonApiDotNetCore/Queries/FieldSelection.cs @@ -23,7 +23,7 @@ public IReadOnlySet GetResourceTypes() public FieldSelectors GetOrCreateSelectors(ResourceType resourceType) #pragma warning restore AV1130 // Return type in method signature should be an interface to an unchangeable collection { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); if (!ContainsKey(resourceType)) { diff --git a/src/JsonApiDotNetCore/Queries/FieldSelectors.cs b/src/JsonApiDotNetCore/Queries/FieldSelectors.cs index a07b4f0c79..ffd95c01bc 100644 --- a/src/JsonApiDotNetCore/Queries/FieldSelectors.cs +++ b/src/JsonApiDotNetCore/Queries/FieldSelectors.cs @@ -30,21 +30,21 @@ public bool ContainsOnlyRelationships public bool ContainsField(ResourceFieldAttribute field) { - ArgumentGuard.NotNull(field, nameof(field)); + ArgumentGuard.NotNull(field); return ContainsKey(field); } public void IncludeAttribute(AttrAttribute attribute) { - ArgumentGuard.NotNull(attribute, nameof(attribute)); + ArgumentGuard.NotNull(attribute); this[attribute] = null; } public void IncludeAttributes(IEnumerable attributes) { - ArgumentGuard.NotNull(attributes, nameof(attributes)); + ArgumentGuard.NotNull(attributes); foreach (AttrAttribute attribute in attributes) { @@ -54,7 +54,7 @@ public void IncludeAttributes(IEnumerable attributes) public void IncludeRelationship(RelationshipAttribute relationship, QueryLayer? queryLayer) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); this[relationship] = queryLayer; } diff --git a/src/JsonApiDotNetCore/Queries/Internal/EvaluatedIncludeCache.cs b/src/JsonApiDotNetCore/Queries/Internal/EvaluatedIncludeCache.cs index 509baf73ee..bbd383fa28 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/EvaluatedIncludeCache.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/EvaluatedIncludeCache.cs @@ -10,7 +10,7 @@ internal sealed class EvaluatedIncludeCache : IEvaluatedIncludeCache /// public void Set(IncludeExpression include) { - ArgumentGuard.NotNull(include, nameof(include)); + ArgumentGuard.NotNull(include); _include = include; } diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs index 705f057bc5..c68e0f77f7 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs @@ -18,7 +18,7 @@ public class FilterParser : QueryExpressionParser public FilterParser(IResourceFactory resourceFactory, Action? validateSingleFieldCallback = null) { - ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory)); + ArgumentGuard.NotNull(resourceFactory); _resourceFactory = resourceFactory; _validateSingleFieldCallback = validateSingleFieldCallback; @@ -26,7 +26,7 @@ public FilterParser(IResourceFactory resourceFactory, Action { diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs index 14d2f1ec15..7418be160f 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs @@ -15,7 +15,7 @@ public class IncludeParser : QueryExpressionParser public IncludeExpression Parse(string source, ResourceType resourceTypeInScope, int? maximumDepth) { - ArgumentGuard.NotNull(resourceTypeInScope, nameof(resourceTypeInScope)); + ArgumentGuard.NotNull(resourceTypeInScope); Tokenize(source); @@ -266,7 +266,7 @@ private sealed class HiddenRootRelationshipAttribute : RelationshipAttribute { public HiddenRootRelationshipAttribute(ResourceType rightType) { - ArgumentGuard.NotNull(rightType, nameof(rightType)); + ArgumentGuard.NotNull(rightType); RightType = rightType; PublicName = "<>"; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/PaginationParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/PaginationParser.cs index 29c7713b11..50b542de6e 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/PaginationParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/PaginationParser.cs @@ -19,7 +19,7 @@ public PaginationParser(Action? va public PaginationQueryStringValueExpression Parse(string source, ResourceType resourceTypeInScope) { - ArgumentGuard.NotNull(resourceTypeInScope, nameof(resourceTypeInScope)); + ArgumentGuard.NotNull(resourceTypeInScope); _resourceTypeInScope = resourceTypeInScope; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryStringParameterScopeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryStringParameterScopeParser.cs index 3cba8e4515..ef95b3ed92 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryStringParameterScopeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryStringParameterScopeParser.cs @@ -22,7 +22,7 @@ public QueryStringParameterScopeParser(FieldChainRequirements chainRequirements, public QueryStringParameterScopeExpression Parse(string source, ResourceType resourceTypeInScope) { - ArgumentGuard.NotNull(resourceTypeInScope, nameof(resourceTypeInScope)); + ArgumentGuard.NotNull(resourceTypeInScope); _resourceTypeInScope = resourceTypeInScope; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryTokenizer.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryTokenizer.cs index 3f04ce92aa..cd920554c9 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryTokenizer.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/QueryTokenizer.cs @@ -27,7 +27,7 @@ public sealed class QueryTokenizer public QueryTokenizer(string source) { - ArgumentGuard.NotNull(source, nameof(source)); + ArgumentGuard.NotNull(source); _source = source; } diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SortParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SortParser.cs index 84782c2b3e..7f4a142ef0 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SortParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SortParser.cs @@ -19,7 +19,7 @@ public SortParser(Action? validate public SortExpression Parse(string source, ResourceType resourceTypeInScope) { - ArgumentGuard.NotNull(resourceTypeInScope, nameof(resourceTypeInScope)); + ArgumentGuard.NotNull(resourceTypeInScope); _resourceTypeInScope = resourceTypeInScope; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldSetParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldSetParser.cs index b4e54f0c46..0cabbcf76e 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldSetParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldSetParser.cs @@ -19,7 +19,7 @@ public SparseFieldSetParser(Action public SparseFieldSetExpression? Parse(string source, ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); _resourceType = resourceType; diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldTypeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldTypeParser.cs index b23dfdfea1..eceb05d211 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldTypeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/SparseFieldTypeParser.cs @@ -12,7 +12,7 @@ public class SparseFieldTypeParser : QueryExpressionParser public SparseFieldTypeParser(IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(resourceGraph); _resourceGraph = resourceGraph; } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs index 40af882044..29e0935954 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs @@ -25,13 +25,13 @@ public QueryLayerComposer(IEnumerable constraintProvid IJsonApiOptions options, IPaginationContext paginationContext, ITargetedFields targetedFields, IEvaluatedIncludeCache evaluatedIncludeCache, ISparseFieldSetCache sparseFieldSetCache) { - ArgumentGuard.NotNull(constraintProviders, nameof(constraintProviders)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(paginationContext, nameof(paginationContext)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); - ArgumentGuard.NotNull(evaluatedIncludeCache, nameof(evaluatedIncludeCache)); - ArgumentGuard.NotNull(sparseFieldSetCache, nameof(sparseFieldSetCache)); + ArgumentGuard.NotNull(constraintProviders); + ArgumentGuard.NotNull(resourceDefinitionAccessor); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(paginationContext); + ArgumentGuard.NotNull(targetedFields); + ArgumentGuard.NotNull(evaluatedIncludeCache); + ArgumentGuard.NotNull(sparseFieldSetCache); _constraintProviders = constraintProviders; _resourceDefinitionAccessor = resourceDefinitionAccessor; @@ -65,7 +65,7 @@ public QueryLayerComposer(IEnumerable constraintProvid /// public FilterExpression? GetSecondaryFilterFromConstraints(TId primaryId, HasManyAttribute hasManyRelationship) { - ArgumentGuard.NotNull(hasManyRelationship, nameof(hasManyRelationship)); + ArgumentGuard.NotNull(hasManyRelationship); if (hasManyRelationship.InverseNavigationProperty == null) { @@ -131,7 +131,7 @@ private static FilterExpression GetInverseHasManyRelationshipFilter(TId pri /// public QueryLayer ComposeFromConstraints(ResourceType requestResourceType) { - ArgumentGuard.NotNull(requestResourceType, nameof(requestResourceType)); + ArgumentGuard.NotNull(requestResourceType); ExpressionInScope[] constraints = _constraintProviders.SelectMany(provider => provider.GetConstraints()).ToArray(); @@ -268,7 +268,7 @@ private static IImmutableSet ApplyIncludeElementUpdate /// public QueryLayer ComposeForGetById(TId id, ResourceType primaryResourceType, TopFieldSelection fieldSelection) { - ArgumentGuard.NotNull(primaryResourceType, nameof(primaryResourceType)); + ArgumentGuard.NotNull(primaryResourceType); AttrAttribute idAttribute = GetIdAttribute(primaryResourceType); @@ -296,7 +296,7 @@ public QueryLayer ComposeForGetById(TId id, ResourceType primaryResourceTyp /// public QueryLayer ComposeSecondaryLayerForRelationship(ResourceType secondaryResourceType) { - ArgumentGuard.NotNull(secondaryResourceType, nameof(secondaryResourceType)); + ArgumentGuard.NotNull(secondaryResourceType); QueryLayer secondaryLayer = ComposeFromConstraints(secondaryResourceType); secondaryLayer.Selection = GetSelectionForRelationship(secondaryResourceType); @@ -320,9 +320,9 @@ private FieldSelection GetSelectionForRelationship(ResourceType secondaryResourc public QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, ResourceType primaryResourceType, TId primaryId, RelationshipAttribute relationship) { - ArgumentGuard.NotNull(secondaryLayer, nameof(secondaryLayer)); - ArgumentGuard.NotNull(primaryResourceType, nameof(primaryResourceType)); - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(secondaryLayer); + ArgumentGuard.NotNull(primaryResourceType); + ArgumentGuard.NotNull(relationship); IncludeExpression? innerInclude = secondaryLayer.Include; secondaryLayer.Include = null; @@ -377,7 +377,7 @@ private IncludeExpression RewriteIncludeForSecondaryEndpoint(IncludeExpression? /// public QueryLayer ComposeForUpdate(TId id, ResourceType primaryResourceType) { - ArgumentGuard.NotNull(primaryResourceType, nameof(primaryResourceType)); + ArgumentGuard.NotNull(primaryResourceType); IImmutableSet includeElements = _targetedFields.Relationships .Select(relationship => new IncludeElementExpression(relationship)).ToImmutableHashSet(); @@ -397,7 +397,7 @@ public QueryLayer ComposeForUpdate(TId id, ResourceType primaryResourceType /// public IEnumerable<(QueryLayer, RelationshipAttribute)> ComposeForGetTargetedSecondaryResourceIds(IIdentifiable primaryResource) { - ArgumentGuard.NotNull(primaryResource, nameof(primaryResource)); + ArgumentGuard.NotNull(primaryResource); foreach (RelationshipAttribute relationship in _targetedFields.Relationships) { @@ -415,8 +415,8 @@ public QueryLayer ComposeForUpdate(TId id, ResourceType primaryResourceType /// public QueryLayer ComposeForGetRelationshipRightIds(RelationshipAttribute relationship, ICollection rightResourceIds) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(relationship); + ArgumentGuard.NotNull(rightResourceIds); AttrAttribute rightIdAttribute = GetIdAttribute(relationship.RightType); @@ -440,8 +440,8 @@ public QueryLayer ComposeForGetRelationshipRightIds(RelationshipAttribute relati /// public QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, TId leftId, ICollection rightResourceIds) { - ArgumentGuard.NotNull(hasManyRelationship, nameof(hasManyRelationship)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(hasManyRelationship); + ArgumentGuard.NotNull(rightResourceIds); AttrAttribute leftIdAttribute = GetIdAttribute(hasManyRelationship.LeftType); AttrAttribute rightIdAttribute = GetIdAttribute(hasManyRelationship.RightType); @@ -476,15 +476,15 @@ public QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, T protected virtual IImmutableSet GetIncludeElements(IImmutableSet includeElements, ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); return _resourceDefinitionAccessor.OnApplyIncludes(resourceType, includeElements); } protected virtual FilterExpression? GetFilter(IReadOnlyCollection expressionsInScope, ResourceType resourceType) { - ArgumentGuard.NotNull(expressionsInScope, nameof(expressionsInScope)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(expressionsInScope); + ArgumentGuard.NotNull(resourceType); FilterExpression[] filters = expressionsInScope.OfType().ToArray(); FilterExpression? filter = LogicalExpression.Compose(LogicalOperator.And, filters); @@ -494,8 +494,8 @@ protected virtual IImmutableSet GetIncludeElements(IIm protected virtual SortExpression GetSort(IReadOnlyCollection expressionsInScope, ResourceType resourceType) { - ArgumentGuard.NotNull(expressionsInScope, nameof(expressionsInScope)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(expressionsInScope); + ArgumentGuard.NotNull(resourceType); SortExpression? sort = expressionsInScope.OfType().FirstOrDefault(); @@ -513,8 +513,8 @@ protected virtual SortExpression GetSort(IReadOnlyCollection ex protected virtual PaginationExpression GetPagination(IReadOnlyCollection expressionsInScope, ResourceType resourceType) { - ArgumentGuard.NotNull(expressionsInScope, nameof(expressionsInScope)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(expressionsInScope); + ArgumentGuard.NotNull(resourceType); PaginationExpression? pagination = expressionsInScope.OfType().FirstOrDefault(); @@ -529,7 +529,7 @@ protected virtual PaginationExpression GetPagination(IReadOnlyCollection public IncludeClauseBuilder(Expression source, LambdaScope lambdaScope, ResourceType resourceType) : base(lambdaScope) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(resourceType); _source = source; _resourceType = resourceType; @@ -30,7 +30,7 @@ public IncludeClauseBuilder(Expression source, LambdaScope lambdaScope, Resource public Expression ApplyInclude(IncludeExpression include) { - ArgumentGuard.NotNull(include, nameof(include)); + ArgumentGuard.NotNull(include); return Visit(include, null); } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameFactory.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameFactory.cs index 864b71c843..32691e05ab 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameFactory.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameFactory.cs @@ -13,7 +13,7 @@ public sealed class LambdaParameterNameFactory public LambdaParameterNameScope Create(string typeName) { - ArgumentGuard.NotNullNorEmpty(typeName, nameof(typeName)); + ArgumentGuard.NotNullNorEmpty(typeName); string parameterName = typeName.Camelize(); parameterName = EnsureNameIsUnique(parameterName); diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameScope.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameScope.cs index 2bad41d310..031dae0a0f 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameScope.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaParameterNameScope.cs @@ -11,8 +11,8 @@ public sealed class LambdaParameterNameScope : IDisposable public LambdaParameterNameScope(string name, LambdaParameterNameFactory owner) { - ArgumentGuard.NotNullNorEmpty(name, nameof(name)); - ArgumentGuard.NotNull(owner, nameof(owner)); + ArgumentGuard.NotNullNorEmpty(name); + ArgumentGuard.NotNull(owner); Name = name; _owner = owner; diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScope.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScope.cs index e5502031a3..52caddbe62 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScope.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScope.cs @@ -23,8 +23,8 @@ private LambdaScope(LambdaParameterNameScope parameterNameScope, ParameterExpres public static LambdaScope Create(LambdaParameterNameFactory nameFactory, Type elementType, Expression? accessorExpression) { - ArgumentGuard.NotNull(nameFactory, nameof(nameFactory)); - ArgumentGuard.NotNull(elementType, nameof(elementType)); + ArgumentGuard.NotNull(nameFactory); + ArgumentGuard.NotNull(elementType); LambdaParameterNameScope parameterNameScope = nameFactory.Create(elementType.Name); ParameterExpression parameter = Expression.Parameter(elementType, parameterNameScope.Name); @@ -35,7 +35,7 @@ public static LambdaScope Create(LambdaParameterNameFactory nameFactory, Type el public LambdaScope WithAccessor(Expression accessorExpression) { - ArgumentGuard.NotNull(accessorExpression, nameof(accessorExpression)); + ArgumentGuard.NotNull(accessorExpression); return new LambdaScope(_parameterNameScope, Parameter, accessorExpression); } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScopeFactory.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScopeFactory.cs index 9c13a63d28..6e4955cf40 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScopeFactory.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/LambdaScopeFactory.cs @@ -10,14 +10,14 @@ public sealed class LambdaScopeFactory public LambdaScopeFactory(LambdaParameterNameFactory nameFactory) { - ArgumentGuard.NotNull(nameFactory, nameof(nameFactory)); + ArgumentGuard.NotNull(nameFactory); _nameFactory = nameFactory; } public LambdaScope CreateScope(Type elementType, Expression? accessorExpression = null) { - ArgumentGuard.NotNull(elementType, nameof(elementType)); + ArgumentGuard.NotNull(elementType); return LambdaScope.Create(_nameFactory, elementType, accessorExpression); } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/OrderClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/OrderClauseBuilder.cs index 7ae8dd2392..775893adcc 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/OrderClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/OrderClauseBuilder.cs @@ -17,8 +17,8 @@ public class OrderClauseBuilder : QueryClauseBuilder public OrderClauseBuilder(Expression source, LambdaScope lambdaScope, Type extensionType) : base(lambdaScope) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(extensionType, nameof(extensionType)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(extensionType); _source = source; _extensionType = extensionType; @@ -26,7 +26,7 @@ public OrderClauseBuilder(Expression source, LambdaScope lambdaScope, Type exten public Expression ApplyOrderBy(SortExpression expression) { - ArgumentGuard.NotNull(expression, nameof(expression)); + ArgumentGuard.NotNull(expression); return Visit(expression, null); } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryClauseBuilder.cs index d04ff57e9d..fdbb3bc0c3 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryClauseBuilder.cs @@ -14,7 +14,7 @@ public abstract class QueryClauseBuilder : QueryExpressionVisitor(Expression accessorExpression, Func action) { - ArgumentGuard.NotNull(accessorExpression, nameof(accessorExpression)); - ArgumentGuard.NotNull(action, nameof(action)); + ArgumentGuard.NotNull(accessorExpression); + ArgumentGuard.NotNull(action); LambdaScope backupScope = LambdaScope; diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs index d571ac1dce..a497846285 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/QueryableBuilder.cs @@ -24,12 +24,12 @@ public class QueryableBuilder public QueryableBuilder(Expression source, Type elementType, Type extensionType, LambdaParameterNameFactory nameFactory, IResourceFactory resourceFactory, IModel entityModel, LambdaScopeFactory? lambdaScopeFactory = null) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(elementType, nameof(elementType)); - ArgumentGuard.NotNull(extensionType, nameof(extensionType)); - ArgumentGuard.NotNull(nameFactory, nameof(nameFactory)); - ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory)); - ArgumentGuard.NotNull(entityModel, nameof(entityModel)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(elementType); + ArgumentGuard.NotNull(extensionType); + ArgumentGuard.NotNull(nameFactory); + ArgumentGuard.NotNull(resourceFactory); + ArgumentGuard.NotNull(entityModel); _source = source; _elementType = elementType; @@ -42,7 +42,7 @@ public QueryableBuilder(Expression source, Type elementType, Type extensionType, public virtual Expression ApplyQuery(QueryLayer layer) { - ArgumentGuard.NotNull(layer, nameof(layer)); + ArgumentGuard.NotNull(layer); Expression expression = _source; diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs index 690c49de24..1f1c10301a 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs @@ -31,11 +31,11 @@ public SelectClauseBuilder(Expression source, LambdaScope lambdaScope, IModel en IResourceFactory resourceFactory) : base(lambdaScope) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(entityModel, nameof(entityModel)); - ArgumentGuard.NotNull(extensionType, nameof(extensionType)); - ArgumentGuard.NotNull(nameFactory, nameof(nameFactory)); - ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(entityModel); + ArgumentGuard.NotNull(extensionType); + ArgumentGuard.NotNull(nameFactory); + ArgumentGuard.NotNull(resourceFactory); _source = source; _entityModel = entityModel; @@ -46,7 +46,7 @@ public SelectClauseBuilder(Expression source, LambdaScope lambdaScope, IModel en public Expression ApplySelect(FieldSelection selection, ResourceType resourceType) { - ArgumentGuard.NotNull(selection, nameof(selection)); + ArgumentGuard.NotNull(selection); Expression bodyInitializer = CreateLambdaBodyInitializer(selection, resourceType, LambdaScope, false); @@ -272,7 +272,7 @@ private sealed class PropertySelector public PropertySelector(PropertyInfo property, QueryLayer? nextLayer = null) { - ArgumentGuard.NotNull(property, nameof(property)); + ArgumentGuard.NotNull(property); Property = property; NextLayer = nextLayer; diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SkipTakeClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SkipTakeClauseBuilder.cs index 4bb9bfd6f5..90109dbfec 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SkipTakeClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SkipTakeClauseBuilder.cs @@ -17,8 +17,8 @@ public class SkipTakeClauseBuilder : QueryClauseBuilder public SkipTakeClauseBuilder(Expression source, LambdaScope lambdaScope, Type extensionType) : base(lambdaScope) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(extensionType, nameof(extensionType)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(extensionType); _source = source; _extensionType = extensionType; @@ -26,7 +26,7 @@ public SkipTakeClauseBuilder(Expression source, LambdaScope lambdaScope, Type ex public Expression ApplySkipTake(PaginationExpression expression) { - ArgumentGuard.NotNull(expression, nameof(expression)); + ArgumentGuard.NotNull(expression); return Visit(expression, null); } diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/WhereClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/WhereClauseBuilder.cs index 2806e96da4..1198a488ff 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/WhereClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/WhereClauseBuilder.cs @@ -24,9 +24,9 @@ public class WhereClauseBuilder : QueryClauseBuilder public WhereClauseBuilder(Expression source, LambdaScope lambdaScope, Type extensionType, LambdaParameterNameFactory nameFactory) : base(lambdaScope) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(extensionType, nameof(extensionType)); - ArgumentGuard.NotNull(nameFactory, nameof(nameFactory)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(extensionType); + ArgumentGuard.NotNull(nameFactory); _source = source; _extensionType = extensionType; @@ -35,7 +35,7 @@ public WhereClauseBuilder(Expression source, LambdaScope lambdaScope, Type exten public Expression ApplyWhere(FilterExpression filter) { - ArgumentGuard.NotNull(filter, nameof(filter)); + ArgumentGuard.NotNull(filter); LambdaExpression lambda = GetPredicateLambda(filter); diff --git a/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs b/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs index 0cad7968c4..495af2ebc1 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs @@ -18,8 +18,8 @@ public sealed class SparseFieldSetCache : ISparseFieldSetCache public SparseFieldSetCache(IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor) { - ArgumentGuard.NotNull(constraintProviders, nameof(constraintProviders)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); + ArgumentGuard.NotNull(constraintProviders); + ArgumentGuard.NotNull(resourceDefinitionAccessor); _resourceDefinitionAccessor = resourceDefinitionAccessor; _lazySourceTable = new Lazy>>(() => BuildSourceTable(constraintProviders)); @@ -70,7 +70,7 @@ private static void AddSparseFieldsToSet(IImmutableSet s /// public IImmutableSet GetSparseFieldSetForQuery(ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); if (!_visitedTable.ContainsKey(resourceType)) { @@ -93,7 +93,7 @@ public IImmutableSet GetSparseFieldSetForQuery(ResourceT /// public IImmutableSet GetIdAttributeSetForRelationshipQuery(ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id)); var inputExpression = new SparseFieldSetExpression(ImmutableHashSet.Create(idAttribute)); @@ -112,7 +112,7 @@ public IImmutableSet GetIdAttributeSetForRelationshipQuery(Resour /// public IImmutableSet GetSparseFieldSetForSerializer(ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); if (!_visitedTable.ContainsKey(resourceType)) { diff --git a/src/JsonApiDotNetCore/Queries/QueryLayer.cs b/src/JsonApiDotNetCore/Queries/QueryLayer.cs index c460560a33..95d61fd4b8 100644 --- a/src/JsonApiDotNetCore/Queries/QueryLayer.cs +++ b/src/JsonApiDotNetCore/Queries/QueryLayer.cs @@ -21,7 +21,7 @@ public sealed class QueryLayer public QueryLayer(ResourceType resourceType) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); ResourceType = resourceType; } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs index 4fcd3e63d9..18167acbc0 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs @@ -31,7 +31,7 @@ public class FilterQueryStringParameterReader : QueryStringParameterReader, IFil public FilterQueryStringParameterReader(IJsonApiRequest request, IResourceGraph resourceGraph, IResourceFactory resourceFactory, IJsonApiOptions options) : base(request, resourceGraph) { - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(options); _options = options; _scopeParser = new QueryStringParameterScopeParser(FieldChainRequirements.EndsInToMany); @@ -50,7 +50,7 @@ protected void ValidateSingleField(ResourceFieldAttribute field, ResourceType re /// public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttribute) { - ArgumentGuard.NotNull(disableQueryStringAttribute, nameof(disableQueryStringAttribute)); + ArgumentGuard.NotNull(disableQueryStringAttribute); return !IsAtomicOperationsRequest && !disableQueryStringAttribute.ContainsParameter(JsonApiQueryStringParameters.Filter); } @@ -58,7 +58,7 @@ public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttr /// public virtual bool CanRead(string parameterName) { - ArgumentGuard.NotNullNorEmpty(parameterName, nameof(parameterName)); + ArgumentGuard.NotNullNorEmpty(parameterName); bool isNested = parameterName.StartsWith("filter[", StringComparison.Ordinal) && parameterName.EndsWith("]", StringComparison.Ordinal); return parameterName == "filter" || isNested; diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs index 6c8bfa2934..299e8b22b2 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs @@ -23,7 +23,7 @@ public class IncludeQueryStringParameterReader : QueryStringParameterReader, IIn public IncludeQueryStringParameterReader(IJsonApiRequest request, IResourceGraph resourceGraph, IJsonApiOptions options) : base(request, resourceGraph) { - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(options); _options = options; _includeParser = new IncludeParser(); @@ -32,7 +32,7 @@ public IncludeQueryStringParameterReader(IJsonApiRequest request, IResourceGraph /// public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttribute) { - ArgumentGuard.NotNull(disableQueryStringAttribute, nameof(disableQueryStringAttribute)); + ArgumentGuard.NotNull(disableQueryStringAttribute); return !IsAtomicOperationsRequest && !disableQueryStringAttribute.ContainsParameter(JsonApiQueryStringParameters.Include); } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/LegacyFilterNotationConverter.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/LegacyFilterNotationConverter.cs index 68d4555e26..259e4c70f1 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/LegacyFilterNotationConverter.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/LegacyFilterNotationConverter.cs @@ -27,7 +27,7 @@ public sealed class LegacyFilterNotationConverter public IEnumerable ExtractConditions(string parameterValue) { - ArgumentGuard.NotNullNorEmpty(parameterValue, nameof(parameterValue)); + ArgumentGuard.NotNullNorEmpty(parameterValue); if (parameterValue.StartsWith(ExpressionPrefix, StringComparison.Ordinal) || parameterValue.StartsWith(InPrefix, StringComparison.Ordinal) || parameterValue.StartsWith(NotInPrefix, StringComparison.Ordinal)) @@ -45,8 +45,8 @@ public IEnumerable ExtractConditions(string parameterValue) public (string parameterName, string parameterValue) Convert(string parameterName, string parameterValue) { - ArgumentGuard.NotNullNorEmpty(parameterName, nameof(parameterName)); - ArgumentGuard.NotNullNorEmpty(parameterValue, nameof(parameterValue)); + ArgumentGuard.NotNullNorEmpty(parameterName); + ArgumentGuard.NotNullNorEmpty(parameterValue); if (parameterValue.StartsWith(ExpressionPrefix, StringComparison.Ordinal)) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs index 743faee492..416b48f4de 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs @@ -28,7 +28,7 @@ public class PaginationQueryStringParameterReader : QueryStringParameterReader, public PaginationQueryStringParameterReader(IJsonApiRequest request, IResourceGraph resourceGraph, IJsonApiOptions options) : base(request, resourceGraph) { - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(options); _options = options; _paginationParser = new PaginationParser(); @@ -37,7 +37,7 @@ public PaginationQueryStringParameterReader(IJsonApiRequest request, IResourceGr /// public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttribute) { - ArgumentGuard.NotNull(disableQueryStringAttribute, nameof(disableQueryStringAttribute)); + ArgumentGuard.NotNull(disableQueryStringAttribute); return !IsAtomicOperationsRequest && !disableQueryStringAttribute.ContainsParameter(JsonApiQueryStringParameters.Page); } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringParameterReader.cs index 103429aa81..656cbff0cb 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringParameterReader.cs @@ -16,8 +16,8 @@ public abstract class QueryStringParameterReader protected QueryStringParameterReader(IJsonApiRequest request, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(resourceGraph); _resourceGraph = resourceGraph; _isCollectionRequest = request.IsCollection; diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringReader.cs index 78e6fe9e92..b5dda40498 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/QueryStringReader.cs @@ -20,10 +20,10 @@ public class QueryStringReader : IQueryStringReader public QueryStringReader(IJsonApiOptions options, IRequestQueryStringAccessor queryStringAccessor, IEnumerable parameterReaders, ILoggerFactory loggerFactory) { - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(queryStringAccessor, nameof(queryStringAccessor)); - ArgumentGuard.NotNull(parameterReaders, nameof(parameterReaders)); + ArgumentGuard.NotNull(loggerFactory); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(queryStringAccessor); + ArgumentGuard.NotNull(parameterReaders); _options = options; _queryStringAccessor = queryStringAccessor; diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/RequestQueryStringAccessor.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/RequestQueryStringAccessor.cs index 2492f01001..2678627d1c 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/RequestQueryStringAccessor.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/RequestQueryStringAccessor.cs @@ -22,7 +22,7 @@ public IQueryCollection Query public RequestQueryStringAccessor(IHttpContextAccessor httpContextAccessor) { - ArgumentGuard.NotNull(httpContextAccessor, nameof(httpContextAccessor)); + ArgumentGuard.NotNull(httpContextAccessor); _httpContextAccessor = httpContextAccessor; } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/ResourceDefinitionQueryableParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/ResourceDefinitionQueryableParameterReader.cs index 51c5de0583..de02589807 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/ResourceDefinitionQueryableParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/ResourceDefinitionQueryableParameterReader.cs @@ -21,8 +21,8 @@ public class ResourceDefinitionQueryableParameterReader : IResourceDefinitionQue public ResourceDefinitionQueryableParameterReader(IJsonApiRequest request, IResourceDefinitionAccessor resourceDefinitionAccessor) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(resourceDefinitionAccessor); _request = request; _resourceDefinitionAccessor = resourceDefinitionAccessor; diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs index 5fa60e7f66..060d0c9986 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs @@ -40,7 +40,7 @@ protected void ValidateSingleField(ResourceFieldAttribute field, ResourceType re /// public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttribute) { - ArgumentGuard.NotNull(disableQueryStringAttribute, nameof(disableQueryStringAttribute)); + ArgumentGuard.NotNull(disableQueryStringAttribute); return !IsAtomicOperationsRequest && !disableQueryStringAttribute.ContainsParameter(JsonApiQueryStringParameters.Sort); } @@ -48,7 +48,7 @@ public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttr /// public virtual bool CanRead(string parameterName) { - ArgumentGuard.NotNullNorEmpty(parameterName, nameof(parameterName)); + ArgumentGuard.NotNullNorEmpty(parameterName); bool isNested = parameterName.StartsWith("sort[", StringComparison.Ordinal) && parameterName.EndsWith("]", StringComparison.Ordinal); return parameterName == "sort" || isNested; diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs index d8f1e858ea..dadf153f21 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs @@ -46,7 +46,7 @@ protected void ValidateSingleField(ResourceFieldAttribute field, ResourceType re /// public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttribute) { - ArgumentGuard.NotNull(disableQueryStringAttribute, nameof(disableQueryStringAttribute)); + ArgumentGuard.NotNull(disableQueryStringAttribute); return !IsAtomicOperationsRequest && !disableQueryStringAttribute.ContainsParameter(JsonApiQueryStringParameters.Fields); } @@ -54,7 +54,7 @@ public virtual bool IsEnabled(DisableQueryStringAttribute disableQueryStringAttr /// public virtual bool CanRead(string parameterName) { - ArgumentGuard.NotNullNorEmpty(parameterName, nameof(parameterName)); + ArgumentGuard.NotNullNorEmpty(parameterName); return parameterName.StartsWith("fields[", StringComparison.Ordinal) && parameterName.EndsWith("]", StringComparison.Ordinal); } diff --git a/src/JsonApiDotNetCore/Repositories/DbContextExtensions.cs b/src/JsonApiDotNetCore/Repositories/DbContextExtensions.cs index cadbd658a8..f78841defc 100644 --- a/src/JsonApiDotNetCore/Repositories/DbContextExtensions.cs +++ b/src/JsonApiDotNetCore/Repositories/DbContextExtensions.cs @@ -13,8 +13,8 @@ public static class DbContextExtensions /// public static IIdentifiable GetTrackedOrAttach(this DbContext dbContext, IIdentifiable resource) { - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(dbContext); + ArgumentGuard.NotNull(resource); var trackedIdentifiable = (IIdentifiable?)dbContext.GetTrackedIdentifiable(resource); @@ -32,8 +32,8 @@ public static IIdentifiable GetTrackedOrAttach(this DbContext dbContext, IIdenti /// public static object? GetTrackedIdentifiable(this DbContext dbContext, IIdentifiable identifiable) { - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); - ArgumentGuard.NotNull(identifiable, nameof(identifiable)); + ArgumentGuard.NotNull(dbContext); + ArgumentGuard.NotNull(identifiable); Type resourceClrType = identifiable.GetClrType(); string? stringId = identifiable.StringId; @@ -53,7 +53,7 @@ private static bool IsResource(EntityEntry entry, Type resourceClrType, string? /// public static void ResetChangeTracker(this DbContext dbContext) { - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); + ArgumentGuard.NotNull(dbContext); dbContext.ChangeTracker.Clear(); } diff --git a/src/JsonApiDotNetCore/Repositories/DbContextResolver.cs b/src/JsonApiDotNetCore/Repositories/DbContextResolver.cs index c8013a5f0a..99decdec66 100644 --- a/src/JsonApiDotNetCore/Repositories/DbContextResolver.cs +++ b/src/JsonApiDotNetCore/Repositories/DbContextResolver.cs @@ -12,7 +12,7 @@ public sealed class DbContextResolver : IDbContextResolver public DbContextResolver(TDbContext dbContext) { - ArgumentGuard.NotNull(dbContext, nameof(dbContext)); + ArgumentGuard.NotNull(dbContext); _dbContext = dbContext; } diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs index 1b807fd24f..653db6129a 100644 --- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs @@ -41,13 +41,13 @@ public EntityFrameworkCoreRepository(ITargetedFields targetedFields, IDbContextR IResourceFactory resourceFactory, IEnumerable constraintProviders, ILoggerFactory loggerFactory, IResourceDefinitionAccessor resourceDefinitionAccessor) { - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); - ArgumentGuard.NotNull(dbContextResolver, nameof(dbContextResolver)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory)); - ArgumentGuard.NotNull(constraintProviders, nameof(constraintProviders)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); + ArgumentGuard.NotNull(targetedFields); + ArgumentGuard.NotNull(dbContextResolver); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(resourceFactory); + ArgumentGuard.NotNull(constraintProviders); + ArgumentGuard.NotNull(loggerFactory); + ArgumentGuard.NotNull(resourceDefinitionAccessor); _targetedFields = targetedFields; _dbContext = dbContextResolver.GetContext(); @@ -66,7 +66,7 @@ public virtual async Task> GetAsync(QueryLayer qu queryLayer }); - ArgumentGuard.NotNull(queryLayer, nameof(queryLayer)); + ArgumentGuard.NotNull(queryLayer); using (CodeTimingSessionManager.Current.Measure("Repository - Get resource(s)")) { @@ -112,7 +112,7 @@ protected virtual IQueryable ApplyQueryLayer(QueryLayer queryLayer) queryLayer }); - ArgumentGuard.NotNull(queryLayer, nameof(queryLayer)); + ArgumentGuard.NotNull(queryLayer); using (CodeTimingSessionManager.Current.Measure("Convert QueryLayer to System.Expression")) { @@ -178,8 +178,8 @@ public virtual async Task CreateAsync(TResource resourceFromRequest, TResource r resourceForDatabase }); - ArgumentGuard.NotNull(resourceFromRequest, nameof(resourceFromRequest)); - ArgumentGuard.NotNull(resourceForDatabase, nameof(resourceForDatabase)); + ArgumentGuard.NotNull(resourceFromRequest); + ArgumentGuard.NotNull(resourceForDatabase); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Create resource"); @@ -240,7 +240,7 @@ await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, has queryLayer }); - ArgumentGuard.NotNull(queryLayer, nameof(queryLayer)); + ArgumentGuard.NotNull(queryLayer); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Get resource for update"); @@ -257,8 +257,8 @@ public virtual async Task UpdateAsync(TResource resourceFromRequest, TResource r resourceFromDatabase }); - ArgumentGuard.NotNull(resourceFromRequest, nameof(resourceFromRequest)); - ArgumentGuard.NotNull(resourceFromDatabase, nameof(resourceFromDatabase)); + ArgumentGuard.NotNull(resourceFromRequest); + ArgumentGuard.NotNull(resourceFromDatabase); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Update resource"); @@ -394,7 +394,7 @@ public virtual async Task SetRelationshipAsync(TResource leftResource, object? r rightValue }); - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); + ArgumentGuard.NotNull(leftResource); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Set relationship"); @@ -425,7 +425,7 @@ public virtual async Task AddToToManyRelationshipAsync(TResource? leftResource, rightResourceIds }); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(rightResourceIds); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Add to to-many relationship"); @@ -488,8 +488,8 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource leftResour rightResourceIds }); - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(leftResource); + ArgumentGuard.NotNull(rightResourceIds); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Remove from to-many relationship"); diff --git a/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs b/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs index 9d42788940..97fcd5ff58 100644 --- a/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs +++ b/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs @@ -19,9 +19,9 @@ public class ResourceRepositoryAccessor : IResourceRepositoryAccessor public ResourceRepositoryAccessor(IServiceProvider serviceProvider, IResourceGraph resourceGraph, IJsonApiRequest request) { - ArgumentGuard.NotNull(serviceProvider, nameof(serviceProvider)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(request, nameof(request)); + ArgumentGuard.NotNull(serviceProvider); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(request); _serviceProvider = serviceProvider; _resourceGraph = resourceGraph; @@ -39,7 +39,7 @@ public async Task> GetAsync(QueryLayer /// public async Task> GetAsync(ResourceType resourceType, QueryLayer queryLayer, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic repository = ResolveReadRepository(resourceType); return (IReadOnlyCollection)await repository.GetAsync(queryLayer, cancellationToken); diff --git a/src/JsonApiDotNetCore/Resources/AbstractResourceWrapper.cs b/src/JsonApiDotNetCore/Resources/AbstractResourceWrapper.cs index 0d294feb57..c3a193d3cd 100644 --- a/src/JsonApiDotNetCore/Resources/AbstractResourceWrapper.cs +++ b/src/JsonApiDotNetCore/Resources/AbstractResourceWrapper.cs @@ -8,7 +8,7 @@ internal sealed class AbstractResourceWrapper : Identifiable, IAbstrac public AbstractResourceWrapper(Type abstractType) { - ArgumentGuard.NotNull(abstractType, nameof(abstractType)); + ArgumentGuard.NotNull(abstractType); AbstractType = abstractType; } diff --git a/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs b/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs index 8c9d4b6a36..9a1c025214 100644 --- a/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs +++ b/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs @@ -9,7 +9,7 @@ internal static class IdentifiableExtensions public static object GetTypedId(this IIdentifiable identifiable) { - ArgumentGuard.NotNull(identifiable, nameof(identifiable)); + ArgumentGuard.NotNull(identifiable); PropertyInfo? property = identifiable.GetClrType().GetProperty(IdPropertyName); @@ -37,7 +37,7 @@ public static object GetTypedId(this IIdentifiable identifiable) public static Type GetClrType(this IIdentifiable identifiable) { - ArgumentGuard.NotNull(identifiable, nameof(identifiable)); + ArgumentGuard.NotNull(identifiable); return identifiable is IAbstractResourceWrapper abstractResource ? abstractResource.AbstractType : identifiable.GetType(); } diff --git a/src/JsonApiDotNetCore/Resources/JsonApiResourceDefinition.cs b/src/JsonApiDotNetCore/Resources/JsonApiResourceDefinition.cs index dbb90bf6fe..fa693d205c 100644 --- a/src/JsonApiDotNetCore/Resources/JsonApiResourceDefinition.cs +++ b/src/JsonApiDotNetCore/Resources/JsonApiResourceDefinition.cs @@ -26,7 +26,7 @@ public class JsonApiResourceDefinition : IResourceDefinition(); @@ -65,7 +65,7 @@ public virtual IImmutableSet OnApplyIncludes(IImmutabl /// protected SortExpression CreateSortExpressionFromLambda(PropertySortOrder keySelectors) { - ArgumentGuard.NotNullNorEmpty(keySelectors, nameof(keySelectors)); + ArgumentGuard.NotNullNorEmpty(keySelectors); ImmutableArray.Builder elementsBuilder = ImmutableArray.CreateBuilder(keySelectors.Count); var lambdaConverter = new SortExpressionLambdaConverter(ResourceGraph); diff --git a/src/JsonApiDotNetCore/Resources/OperationContainer.cs b/src/JsonApiDotNetCore/Resources/OperationContainer.cs index d2fa2c0d3e..a066943c82 100644 --- a/src/JsonApiDotNetCore/Resources/OperationContainer.cs +++ b/src/JsonApiDotNetCore/Resources/OperationContainer.cs @@ -18,9 +18,9 @@ public sealed class OperationContainer public OperationContainer(IIdentifiable resource, ITargetedFields targetedFields, IJsonApiRequest request) { - ArgumentGuard.NotNull(resource, nameof(resource)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); - ArgumentGuard.NotNull(request, nameof(request)); + ArgumentGuard.NotNull(resource); + ArgumentGuard.NotNull(targetedFields); + ArgumentGuard.NotNull(request); Resource = resource; TargetedFields = targetedFields; @@ -34,7 +34,7 @@ public void SetTransactionId(string transactionId) public OperationContainer WithResource(IIdentifiable resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); return new OperationContainer(resource, TargetedFields, Request); } diff --git a/src/JsonApiDotNetCore/Resources/ResourceChangeTracker.cs b/src/JsonApiDotNetCore/Resources/ResourceChangeTracker.cs index 658e5e2c5b..89ba115a64 100644 --- a/src/JsonApiDotNetCore/Resources/ResourceChangeTracker.cs +++ b/src/JsonApiDotNetCore/Resources/ResourceChangeTracker.cs @@ -19,8 +19,8 @@ public sealed class ResourceChangeTracker : IResourceChangeTracker public void SetInitiallyStoredAttributeValues(TResource resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); _initiallyStoredAttributeValues = CreateAttributeDictionary(resource, _request.PrimaryResourceType!.Attributes); } @@ -37,7 +37,7 @@ public void SetInitiallyStoredAttributeValues(TResource resource) /// public void SetRequestAttributeValues(TResource resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); _requestAttributeValues = CreateAttributeDictionary(resource, _targetedFields.Attributes); } @@ -45,7 +45,7 @@ public void SetRequestAttributeValues(TResource resource) /// public void SetFinallyStoredAttributeValues(TResource resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); _finallyStoredAttributeValues = CreateAttributeDictionary(resource, _request.PrimaryResourceType!.Attributes); } diff --git a/src/JsonApiDotNetCore/Resources/ResourceDefinitionAccessor.cs b/src/JsonApiDotNetCore/Resources/ResourceDefinitionAccessor.cs index 9f8dfdddeb..79b48c99eb 100644 --- a/src/JsonApiDotNetCore/Resources/ResourceDefinitionAccessor.cs +++ b/src/JsonApiDotNetCore/Resources/ResourceDefinitionAccessor.cs @@ -17,8 +17,8 @@ public class ResourceDefinitionAccessor : IResourceDefinitionAccessor public ResourceDefinitionAccessor(IResourceGraph resourceGraph, IServiceProvider serviceProvider) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(serviceProvider, nameof(serviceProvider)); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(serviceProvider); _resourceGraph = resourceGraph; _serviceProvider = serviceProvider; @@ -27,7 +27,7 @@ public ResourceDefinitionAccessor(IResourceGraph resourceGraph, IServiceProvider /// public IImmutableSet OnApplyIncludes(ResourceType resourceType, IImmutableSet existingIncludes) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.OnApplyIncludes(existingIncludes); @@ -36,7 +36,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public FilterExpression? OnApplyFilter(ResourceType resourceType, FilterExpression? existingFilter) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.OnApplyFilter(existingFilter); @@ -45,7 +45,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public SortExpression? OnApplySort(ResourceType resourceType, SortExpression? existingSort) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.OnApplySort(existingSort); @@ -54,7 +54,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public PaginationExpression? OnApplyPagination(ResourceType resourceType, PaginationExpression? existingPagination) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.OnApplyPagination(existingPagination); @@ -63,7 +63,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public SparseFieldSetExpression? OnApplySparseFieldSet(ResourceType resourceType, SparseFieldSetExpression? existingSparseFieldSet) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.OnApplySparseFieldSet(existingSparseFieldSet); @@ -72,8 +72,8 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public object? GetQueryableHandlerForQueryStringParameter(Type resourceClrType, string parameterName) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); - ArgumentGuard.NotNullNorEmpty(parameterName, nameof(parameterName)); + ArgumentGuard.NotNull(resourceClrType); + ArgumentGuard.NotNullNorEmpty(parameterName); dynamic resourceDefinition = ResolveResourceDefinition(resourceClrType); dynamic handlers = resourceDefinition.OnRegisterQueryableHandlersForQueryStringParameters(); @@ -92,7 +92,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso /// public IDictionary? GetMeta(ResourceType resourceType, IIdentifiable resourceInstance) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resourceType); dynamic resourceDefinition = ResolveResourceDefinition(resourceType); return resourceDefinition.GetMeta((dynamic)resourceInstance); @@ -102,7 +102,7 @@ public IImmutableSet OnApplyIncludes(ResourceType reso public async Task OnPrepareWriteAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); dynamic resourceDefinition = ResolveResourceDefinition(resource.GetClrType()); await resourceDefinition.OnPrepareWriteAsync((dynamic)resource, writeOperation, cancellationToken); @@ -113,8 +113,8 @@ public async Task OnPrepareWriteAsync(TResource resource, WriteOperat IIdentifiable? rightResourceId, WriteOperationKind writeOperation, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); - ArgumentGuard.NotNull(hasOneRelationship, nameof(hasOneRelationship)); + ArgumentGuard.NotNull(leftResource); + ArgumentGuard.NotNull(hasOneRelationship); dynamic resourceDefinition = ResolveResourceDefinition(leftResource.GetClrType()); @@ -127,9 +127,9 @@ public async Task OnSetToManyRelationshipAsync(TResource leftResource ISet rightResourceIds, WriteOperationKind writeOperation, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); - ArgumentGuard.NotNull(hasManyRelationship, nameof(hasManyRelationship)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(leftResource); + ArgumentGuard.NotNull(hasManyRelationship); + ArgumentGuard.NotNull(rightResourceIds); dynamic resourceDefinition = ResolveResourceDefinition(leftResource.GetClrType()); await resourceDefinition.OnSetToManyRelationshipAsync((dynamic)leftResource, hasManyRelationship, rightResourceIds, writeOperation, cancellationToken); @@ -140,8 +140,8 @@ public async Task OnAddToRelationshipAsync(TResource leftResource, Ha CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(hasManyRelationship, nameof(hasManyRelationship)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(hasManyRelationship); + ArgumentGuard.NotNull(rightResourceIds); dynamic resourceDefinition = ResolveResourceDefinition(leftResource.GetClrType()); await resourceDefinition.OnAddToRelationshipAsync((dynamic)leftResource, hasManyRelationship, rightResourceIds, cancellationToken); @@ -152,9 +152,9 @@ public async Task OnRemoveFromRelationshipAsync(TResource leftResourc ISet rightResourceIds, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); - ArgumentGuard.NotNull(hasManyRelationship, nameof(hasManyRelationship)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNull(leftResource); + ArgumentGuard.NotNull(hasManyRelationship); + ArgumentGuard.NotNull(rightResourceIds); dynamic resourceDefinition = ResolveResourceDefinition(leftResource.GetClrType()); await resourceDefinition.OnRemoveFromRelationshipAsync((dynamic)leftResource, hasManyRelationship, rightResourceIds, cancellationToken); @@ -164,7 +164,7 @@ public async Task OnRemoveFromRelationshipAsync(TResource leftResourc public async Task OnWritingAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); dynamic resourceDefinition = ResolveResourceDefinition(resource.GetClrType()); await resourceDefinition.OnWritingAsync((dynamic)resource, writeOperation, cancellationToken); @@ -174,7 +174,7 @@ public async Task OnWritingAsync(TResource resource, WriteOperationKi public async Task OnWriteSucceededAsync(TResource resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) where TResource : class, IIdentifiable { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); dynamic resourceDefinition = ResolveResourceDefinition(resource.GetClrType()); await resourceDefinition.OnWriteSucceededAsync((dynamic)resource, writeOperation, cancellationToken); @@ -183,7 +183,7 @@ public async Task OnWriteSucceededAsync(TResource resource, WriteOper /// public void OnDeserialize(IIdentifiable resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); dynamic resourceDefinition = ResolveResourceDefinition(resource.GetClrType()); resourceDefinition.OnDeserialize((dynamic)resource); @@ -192,7 +192,7 @@ public void OnDeserialize(IIdentifiable resource) /// public void OnSerialize(IIdentifiable resource) { - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); dynamic resourceDefinition = ResolveResourceDefinition(resource.GetClrType()); resourceDefinition.OnSerialize((dynamic)resource); diff --git a/src/JsonApiDotNetCore/Resources/ResourceFactory.cs b/src/JsonApiDotNetCore/Resources/ResourceFactory.cs index c276c073cd..27ddc317d8 100644 --- a/src/JsonApiDotNetCore/Resources/ResourceFactory.cs +++ b/src/JsonApiDotNetCore/Resources/ResourceFactory.cs @@ -15,7 +15,7 @@ internal sealed class ResourceFactory : IResourceFactory public ResourceFactory(IServiceProvider serviceProvider) { - ArgumentGuard.NotNull(serviceProvider, nameof(serviceProvider)); + ArgumentGuard.NotNull(serviceProvider); _serviceProvider = serviceProvider; } @@ -23,7 +23,7 @@ public ResourceFactory(IServiceProvider serviceProvider) /// public IIdentifiable CreateInstance(Type resourceClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); if (!resourceClrType.IsAssignableTo(typeof(IIdentifiable))) { @@ -85,7 +85,7 @@ private static IIdentifiable InnerCreateInstance(Type type, IServiceProvider ser /// public NewExpression CreateNewExpression(Type resourceClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); if (HasSingleConstructorWithoutParameters(resourceClrType)) { diff --git a/src/JsonApiDotNetCore/Resources/SortExpressionLambdaConverter.cs b/src/JsonApiDotNetCore/Resources/SortExpressionLambdaConverter.cs index 36a6d97aec..f1efa204d4 100644 --- a/src/JsonApiDotNetCore/Resources/SortExpressionLambdaConverter.cs +++ b/src/JsonApiDotNetCore/Resources/SortExpressionLambdaConverter.cs @@ -15,14 +15,14 @@ internal sealed class SortExpressionLambdaConverter public SortExpressionLambdaConverter(IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(resourceGraph); _resourceGraph = resourceGraph; } public SortElementExpression FromLambda(Expression> keySelector, ListSortDirection sortDirection) { - ArgumentGuard.NotNull(keySelector, nameof(keySelector)); + ArgumentGuard.NotNull(keySelector); _fields.Clear(); diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs index 005e423add..25218b1ba9 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs @@ -27,7 +27,7 @@ public sealed class ResourceObjectConverter : JsonObjectConverter public AtomicReferenceResult Convert(AtomicReference atomicReference, ResourceIdentityRequirements requirements, RequestAdapterState state) { - ArgumentGuard.NotNull(atomicReference, nameof(atomicReference)); - ArgumentGuard.NotNull(requirements, nameof(requirements)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(atomicReference); + ArgumentGuard.NotNull(requirements); + ArgumentGuard.NotNull(state); using IDisposable _ = state.Position.PushElement("ref"); (IIdentifiable resource, ResourceType resourceType) = ConvertResourceIdentity(atomicReference, requirements, state); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceResult.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceResult.cs index 9e6d982e21..a15ecd8e0c 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceResult.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceResult.cs @@ -17,8 +17,8 @@ public sealed class AtomicReferenceResult public AtomicReferenceResult(IIdentifiable resource, ResourceType resourceType, RelationshipAttribute? relationship) { - ArgumentGuard.NotNull(resource, nameof(resource)); - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); + ArgumentGuard.NotNull(resource); + ArgumentGuard.NotNull(resourceType); Resource = resource; ResourceType = resourceType; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentAdapter.cs index 369f9076d2..43e4a7a78b 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentAdapter.cs @@ -16,10 +16,10 @@ public DocumentAdapter(IJsonApiRequest request, ITargetedFields targetedFields, IDocumentInResourceOrRelationshipRequestAdapter documentInResourceOrRelationshipRequestAdapter, IDocumentInOperationsRequestAdapter documentInOperationsRequestAdapter) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); - ArgumentGuard.NotNull(documentInResourceOrRelationshipRequestAdapter, nameof(documentInResourceOrRelationshipRequestAdapter)); - ArgumentGuard.NotNull(documentInOperationsRequestAdapter, nameof(documentInOperationsRequestAdapter)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(targetedFields); + ArgumentGuard.NotNull(documentInResourceOrRelationshipRequestAdapter); + ArgumentGuard.NotNull(documentInOperationsRequestAdapter); _request = request; _targetedFields = targetedFields; @@ -30,7 +30,7 @@ public DocumentAdapter(IJsonApiRequest request, ITargetedFields targetedFields, /// public object? Convert(Document document) { - ArgumentGuard.NotNull(document, nameof(document)); + ArgumentGuard.NotNull(document); using var adapterState = new RequestAdapterState(_request, _targetedFields); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInOperationsRequestAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInOperationsRequestAdapter.cs index 8a50db9fec..2a272c4cfd 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInOperationsRequestAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInOperationsRequestAdapter.cs @@ -13,8 +13,8 @@ public sealed class DocumentInOperationsRequestAdapter : BaseAdapter, IDocumentI public DocumentInOperationsRequestAdapter(IJsonApiOptions options, IAtomicOperationObjectAdapter atomicOperationObjectAdapter) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(atomicOperationObjectAdapter, nameof(atomicOperationObjectAdapter)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(atomicOperationObjectAdapter); _options = options; _atomicOperationObjectAdapter = atomicOperationObjectAdapter; @@ -23,7 +23,7 @@ public DocumentInOperationsRequestAdapter(IJsonApiOptions options, IAtomicOperat /// public IReadOnlyList Convert(Document document, RequestAdapterState state) { - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(state); AssertHasOperations(document.Operations, state); using IDisposable _ = state.Position.PushElement("atomic:operations"); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs index aaf5b813c8..f5d4cb088c 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs @@ -15,9 +15,9 @@ public sealed class DocumentInResourceOrRelationshipRequestAdapter : IDocumentIn public DocumentInResourceOrRelationshipRequestAdapter(IJsonApiOptions options, IResourceDataAdapter resourceDataAdapter, IRelationshipDataAdapter relationshipDataAdapter) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(resourceDataAdapter, nameof(resourceDataAdapter)); - ArgumentGuard.NotNull(relationshipDataAdapter, nameof(relationshipDataAdapter)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(resourceDataAdapter); + ArgumentGuard.NotNull(relationshipDataAdapter); _options = options; _resourceDataAdapter = resourceDataAdapter; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs index 89cf3caa18..ac1e25746b 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs @@ -14,7 +14,7 @@ public sealed class RelationshipDataAdapter : BaseAdapter, IRelationshipDataAdap public RelationshipDataAdapter(IResourceIdentifierObjectAdapter resourceIdentifierObjectAdapter) { - ArgumentGuard.NotNull(resourceIdentifierObjectAdapter, nameof(resourceIdentifierObjectAdapter)); + ArgumentGuard.NotNull(resourceIdentifierObjectAdapter); _resourceIdentifierObjectAdapter = resourceIdentifierObjectAdapter; } @@ -61,8 +61,8 @@ private static SingleOrManyData ToIdentifierData(Singl public object? Convert(SingleOrManyData data, RelationshipAttribute relationship, bool useToManyElementType, RequestAdapterState state) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(relationship); + ArgumentGuard.NotNull(state); AssertHasData(data, state); using IDisposable _ = state.Position.PushElement("data"); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterPosition.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterPosition.cs index 3ae7caa6af..644383e711 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterPosition.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterPosition.cs @@ -19,7 +19,7 @@ public RequestAdapterPosition() public IDisposable PushElement(string name) { - ArgumentGuard.NotNullNorEmpty(name, nameof(name)); + ArgumentGuard.NotNullNorEmpty(name); _stack.Push($"/{name}"); return _disposable; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterState.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterState.cs index 88cf686f51..c7d40e1794 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterState.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RequestAdapterState.cs @@ -24,8 +24,8 @@ public sealed class RequestAdapterState : IDisposable public RequestAdapterState(IJsonApiRequest request, ITargetedFields targetedFields) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(targetedFields, nameof(targetedFields)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(targetedFields); InjectableRequest = request; InjectableTargetedFields = targetedFields; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceDataAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceDataAdapter.cs index dc84fbad3d..ffe971f42f 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceDataAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceDataAdapter.cs @@ -12,8 +12,8 @@ public class ResourceDataAdapter : BaseAdapter, IResourceDataAdapter public ResourceDataAdapter(IResourceDefinitionAccessor resourceDefinitionAccessor, IResourceObjectAdapter resourceObjectAdapter) { - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); - ArgumentGuard.NotNull(resourceObjectAdapter, nameof(resourceObjectAdapter)); + ArgumentGuard.NotNull(resourceDefinitionAccessor); + ArgumentGuard.NotNull(resourceObjectAdapter); _resourceDefinitionAccessor = resourceDefinitionAccessor; _resourceObjectAdapter = resourceObjectAdapter; @@ -22,8 +22,8 @@ public ResourceDataAdapter(IResourceDefinitionAccessor resourceDefinitionAccesso /// public IIdentifiable Convert(SingleOrManyData data, ResourceIdentityRequirements requirements, RequestAdapterState state) { - ArgumentGuard.NotNull(requirements, nameof(requirements)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(requirements); + ArgumentGuard.NotNull(state); AssertHasData(data, state); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentifierObjectAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentifierObjectAdapter.cs index d0e1b54856..8032c6c60c 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentifierObjectAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentifierObjectAdapter.cs @@ -15,9 +15,9 @@ public ResourceIdentifierObjectAdapter(IResourceGraph resourceGraph, IResourceFa /// public IIdentifiable Convert(ResourceIdentifierObject resourceIdentifierObject, ResourceIdentityRequirements requirements, RequestAdapterState state) { - ArgumentGuard.NotNull(resourceIdentifierObject, nameof(resourceIdentifierObject)); - ArgumentGuard.NotNull(requirements, nameof(requirements)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(resourceIdentifierObject); + ArgumentGuard.NotNull(requirements); + ArgumentGuard.NotNull(state); (IIdentifiable resource, _) = ConvertResourceIdentity(resourceIdentifierObject, requirements, state); return resource; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs index 61c6cc1857..9df5215da9 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs @@ -18,8 +18,8 @@ public abstract class ResourceIdentityAdapter : BaseAdapter protected ResourceIdentityAdapter(IResourceGraph resourceGraph, IResourceFactory resourceFactory) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(resourceFactory, nameof(resourceFactory)); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(resourceFactory); _resourceGraph = resourceGraph; _resourceFactory = resourceFactory; @@ -28,9 +28,9 @@ protected ResourceIdentityAdapter(IResourceGraph resourceGraph, IResourceFactory protected (IIdentifiable resource, ResourceType resourceType) ConvertResourceIdentity(ResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state) { - ArgumentGuard.NotNull(identity, nameof(identity)); - ArgumentGuard.NotNull(requirements, nameof(requirements)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(identity); + ArgumentGuard.NotNull(requirements); + ArgumentGuard.NotNull(state); ResourceType resourceType = ResolveType(identity, requirements, state); IIdentifiable resource = CreateResource(identity, requirements, resourceType.ClrType, state); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs index 2199782a3a..b489665a41 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs @@ -17,8 +17,8 @@ public ResourceObjectAdapter(IResourceGraph resourceGraph, IResourceFactory reso IRelationshipDataAdapter relationshipDataAdapter) : base(resourceGraph, resourceFactory) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(relationshipDataAdapter, nameof(relationshipDataAdapter)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(relationshipDataAdapter); _options = options; _relationshipDataAdapter = relationshipDataAdapter; @@ -28,9 +28,9 @@ public ResourceObjectAdapter(IResourceGraph resourceGraph, IResourceFactory reso public (IIdentifiable resource, ResourceType resourceType) Convert(ResourceObject resourceObject, ResourceIdentityRequirements requirements, RequestAdapterState state) { - ArgumentGuard.NotNull(resourceObject, nameof(resourceObject)); - ArgumentGuard.NotNull(requirements, nameof(requirements)); - ArgumentGuard.NotNull(state, nameof(state)); + ArgumentGuard.NotNull(resourceObject); + ArgumentGuard.NotNull(requirements); + ArgumentGuard.NotNull(state); (IIdentifiable resource, ResourceType resourceType) = ConvertResourceIdentity(resourceObject, requirements, state); diff --git a/src/JsonApiDotNetCore/Serialization/Request/JsonApiReader.cs b/src/JsonApiDotNetCore/Serialization/Request/JsonApiReader.cs index 0942683487..30e4508894 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/JsonApiReader.cs @@ -25,9 +25,9 @@ public sealed class JsonApiReader : IJsonApiReader public JsonApiReader(IJsonApiOptions options, IDocumentAdapter documentAdapter, ILoggerFactory loggerFactory) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(documentAdapter, nameof(documentAdapter)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(documentAdapter); + ArgumentGuard.NotNull(loggerFactory); _options = options; _documentAdapter = documentAdapter; @@ -37,7 +37,7 @@ public JsonApiReader(IJsonApiOptions options, IDocumentAdapter documentAdapter, /// public async Task ReadAsync(HttpRequest httpRequest) { - ArgumentGuard.NotNull(httpRequest, nameof(httpRequest)); + ArgumentGuard.NotNull(httpRequest); string requestBody = await ReceiveRequestBodyAsync(httpRequest); diff --git a/src/JsonApiDotNetCore/Serialization/Request/JsonInvalidAttributeInfo.cs b/src/JsonApiDotNetCore/Serialization/Request/JsonInvalidAttributeInfo.cs index 02ef36aa53..ed0afee834 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/JsonInvalidAttributeInfo.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/JsonInvalidAttributeInfo.cs @@ -16,8 +16,8 @@ internal sealed class JsonInvalidAttributeInfo public JsonInvalidAttributeInfo(string attributeName, Type attributeType, string? jsonValue, JsonValueKind jsonType) { - ArgumentGuard.NotNullNorEmpty(attributeName, nameof(attributeName)); - ArgumentGuard.NotNull(attributeType, nameof(attributeType)); + ArgumentGuard.NotNullNorEmpty(attributeName); + ArgumentGuard.NotNull(attributeType); AttributeName = attributeName; AttributeType = attributeType; diff --git a/src/JsonApiDotNetCore/Serialization/Request/ModelConversionException.cs b/src/JsonApiDotNetCore/Serialization/Request/ModelConversionException.cs index b43af538e5..cf2428129d 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/ModelConversionException.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/ModelConversionException.cs @@ -18,7 +18,7 @@ public sealed class ModelConversionException : Exception public ModelConversionException(RequestAdapterPosition position, string? genericMessage, string? specificMessage, HttpStatusCode? statusCode = null) : base(genericMessage) { - ArgumentGuard.NotNull(position, nameof(position)); + ArgumentGuard.NotNull(position); GenericMessage = genericMessage; SpecificMessage = specificMessage; diff --git a/src/JsonApiDotNetCore/Serialization/Response/ETagGenerator.cs b/src/JsonApiDotNetCore/Serialization/Response/ETagGenerator.cs index 1352317575..6fc6b7af61 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ETagGenerator.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ETagGenerator.cs @@ -9,7 +9,7 @@ internal sealed class ETagGenerator : IETagGenerator public ETagGenerator(IFingerprintGenerator fingerprintGenerator) { - ArgumentGuard.NotNull(fingerprintGenerator, nameof(fingerprintGenerator)); + ArgumentGuard.NotNull(fingerprintGenerator); _fingerprintGenerator = fingerprintGenerator; } diff --git a/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs b/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs index 0eaee430c3..5f9f7eeb6c 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs @@ -18,7 +18,7 @@ private static uint ToLookupEntry(int index) /// public string Generate(IEnumerable elements) { - ArgumentGuard.NotNull(elements, nameof(elements)); + ArgumentGuard.NotNull(elements); using var hasher = IncrementalHash.CreateHash(HashAlgorithmName.MD5); diff --git a/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs b/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs index 20f4ad242b..8f77a2ff5c 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs @@ -28,12 +28,12 @@ public sealed class JsonApiWriter : IJsonApiWriter public JsonApiWriter(IJsonApiRequest request, IJsonApiOptions options, IResponseModelAdapter responseModelAdapter, IExceptionHandler exceptionHandler, IETagGenerator eTagGenerator, ILoggerFactory loggerFactory) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(responseModelAdapter, nameof(responseModelAdapter)); - ArgumentGuard.NotNull(exceptionHandler, nameof(exceptionHandler)); - ArgumentGuard.NotNull(eTagGenerator, nameof(eTagGenerator)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(responseModelAdapter); + ArgumentGuard.NotNull(exceptionHandler); + ArgumentGuard.NotNull(eTagGenerator); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(loggerFactory); _request = request; _options = options; @@ -46,7 +46,7 @@ public JsonApiWriter(IJsonApiRequest request, IJsonApiOptions options, IResponse /// public async Task WriteAsync(object? model, HttpContext httpContext) { - ArgumentGuard.NotNull(httpContext, nameof(httpContext)); + ArgumentGuard.NotNull(httpContext); if (model == null && !CanWriteBody((HttpStatusCode)httpContext.Response.StatusCode)) { diff --git a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs index 0bad02066b..ea0eb197df 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs @@ -50,11 +50,11 @@ private HttpContext HttpContext public LinkBuilder(IJsonApiOptions options, IJsonApiRequest request, IPaginationContext paginationContext, IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator, IControllerResourceMapping controllerResourceMapping) { - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(paginationContext, nameof(paginationContext)); - ArgumentGuard.NotNull(linkGenerator, nameof(linkGenerator)); - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(paginationContext); + ArgumentGuard.NotNull(linkGenerator); + ArgumentGuard.NotNull(controllerResourceMapping); _options = options; _request = request; @@ -225,8 +225,8 @@ private string GetQueryStringInPaginationLink(int pageOffset, string? pageSizeVa /// public ResourceLinks? GetResourceLinks(ResourceType resourceType, IIdentifiable resource) { - ArgumentGuard.NotNull(resourceType, nameof(resourceType)); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resourceType); + ArgumentGuard.NotNull(resource); var links = new ResourceLinks(); @@ -263,8 +263,8 @@ private bool ShouldIncludeResourceLink(LinkTypes linkType, ResourceType resource /// public RelationshipLinks? GetRelationshipLinks(RelationshipAttribute relationship, IIdentifiable leftResource) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); - ArgumentGuard.NotNull(leftResource, nameof(leftResource)); + ArgumentGuard.NotNull(relationship); + ArgumentGuard.NotNull(leftResource); var links = new RelationshipLinks(); diff --git a/src/JsonApiDotNetCore/Serialization/Response/MetaBuilder.cs b/src/JsonApiDotNetCore/Serialization/Response/MetaBuilder.cs index 91ec62387c..0da0ebe14b 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/MetaBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/MetaBuilder.cs @@ -16,9 +16,9 @@ public sealed class MetaBuilder : IMetaBuilder public MetaBuilder(IPaginationContext paginationContext, IJsonApiOptions options, IResponseMeta responseMeta) { - ArgumentGuard.NotNull(paginationContext, nameof(paginationContext)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(responseMeta, nameof(responseMeta)); + ArgumentGuard.NotNull(paginationContext); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(responseMeta); _paginationContext = paginationContext; _options = options; @@ -28,7 +28,7 @@ public MetaBuilder(IPaginationContext paginationContext, IJsonApiOptions options /// public void Add(IDictionary values) { - ArgumentGuard.NotNull(values, nameof(values)); + ArgumentGuard.NotNull(values); _meta = values.Keys.Union(_meta.Keys).ToDictionary(key => key, key => values.ContainsKey(key) ? values[key] : _meta[key]); } diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs index aed19d9097..01743168be 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs @@ -37,9 +37,9 @@ internal sealed class ResourceObjectTreeNode : IEquatable(); _directChildren.Add(treeNode); @@ -61,7 +61,7 @@ public void AttachDirectChild(ResourceObjectTreeNode treeNode) public void EnsureHasRelationship(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); _childrenByRelationship ??= new Dictionary>(); @@ -73,8 +73,8 @@ public void EnsureHasRelationship(RelationshipAttribute relationship) public void AttachRelationshipChild(RelationshipAttribute relationship, ResourceObjectTreeNode rightNode) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); - ArgumentGuard.NotNull(rightNode, nameof(rightNode)); + ArgumentGuard.NotNull(relationship); + ArgumentGuard.NotNull(rightNode); if (_childrenByRelationship == null) { diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs index 223166e59e..5dc97f8052 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs @@ -37,14 +37,14 @@ public ResponseModelAdapter(IJsonApiRequest request, IJsonApiOptions options, IL IResourceDefinitionAccessor resourceDefinitionAccessor, IEvaluatedIncludeCache evaluatedIncludeCache, ISparseFieldSetCache sparseFieldSetCache, IRequestQueryStringAccessor requestQueryStringAccessor) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(linkBuilder, nameof(linkBuilder)); - ArgumentGuard.NotNull(metaBuilder, nameof(metaBuilder)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); - ArgumentGuard.NotNull(evaluatedIncludeCache, nameof(evaluatedIncludeCache)); - ArgumentGuard.NotNull(sparseFieldSetCache, nameof(sparseFieldSetCache)); - ArgumentGuard.NotNull(requestQueryStringAccessor, nameof(requestQueryStringAccessor)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(linkBuilder); + ArgumentGuard.NotNull(metaBuilder); + ArgumentGuard.NotNull(resourceDefinitionAccessor); + ArgumentGuard.NotNull(evaluatedIncludeCache); + ArgumentGuard.NotNull(sparseFieldSetCache); + ArgumentGuard.NotNull(requestQueryStringAccessor); _request = request; _options = options; diff --git a/src/JsonApiDotNetCore/Services/AsyncCollectionExtensions.cs b/src/JsonApiDotNetCore/Services/AsyncCollectionExtensions.cs index 3924999f63..11e483b419 100644 --- a/src/JsonApiDotNetCore/Services/AsyncCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Services/AsyncCollectionExtensions.cs @@ -7,8 +7,8 @@ public static class AsyncCollectionExtensions { public static async Task AddRangeAsync(this ICollection source, IAsyncEnumerable elementsToAdd, CancellationToken cancellationToken = default) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(elementsToAdd, nameof(elementsToAdd)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(elementsToAdd); await foreach (T missingResource in elementsToAdd.WithCancellation(cancellationToken)) { @@ -18,7 +18,7 @@ public static async Task AddRangeAsync(this ICollection source, IAsyncEnum public static async Task> ToListAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) { - ArgumentGuard.NotNull(source, nameof(source)); + ArgumentGuard.NotNull(source); var list = new List(); diff --git a/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs b/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs index 0cca8b92b9..0d7280e1b4 100644 --- a/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs +++ b/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs @@ -34,14 +34,14 @@ public JsonApiResourceService(IResourceRepositoryAccessor repositoryAccessor, IQ IJsonApiOptions options, ILoggerFactory loggerFactory, IJsonApiRequest request, IResourceChangeTracker resourceChangeTracker, IResourceDefinitionAccessor resourceDefinitionAccessor) { - ArgumentGuard.NotNull(repositoryAccessor, nameof(repositoryAccessor)); - ArgumentGuard.NotNull(queryLayerComposer, nameof(queryLayerComposer)); - ArgumentGuard.NotNull(paginationContext, nameof(paginationContext)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(loggerFactory, nameof(loggerFactory)); - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(resourceChangeTracker, nameof(resourceChangeTracker)); - ArgumentGuard.NotNull(resourceDefinitionAccessor, nameof(resourceDefinitionAccessor)); + ArgumentGuard.NotNull(repositoryAccessor); + ArgumentGuard.NotNull(queryLayerComposer); + ArgumentGuard.NotNull(paginationContext); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(loggerFactory); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(resourceChangeTracker); + ArgumentGuard.NotNull(resourceDefinitionAccessor); _repositoryAccessor = repositoryAccessor; _queryLayerComposer = queryLayerComposer; @@ -145,7 +145,7 @@ public virtual async Task GetAsync(TId id, CancellationToken cancella relationshipName }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); + ArgumentGuard.NotNullNorEmpty(relationshipName); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Service - Get relationship"); @@ -195,7 +195,7 @@ private async Task RetrieveResourceCountForNonPrimaryEndpointAsync(TId id, HasMa resource }); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Service - Create resource"); @@ -339,8 +339,8 @@ public virtual async Task AddToToManyRelationshipAsync(TId leftId, string relati rightResourceIds }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNullNorEmpty(relationshipName); + ArgumentGuard.NotNull(rightResourceIds); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Service - Add to to-many relationship"); @@ -446,7 +446,7 @@ private async Task GetForHasManyUpdateAsync(HasManyAttribute hasManyR resource }); - ArgumentGuard.NotNull(resource, nameof(resource)); + ArgumentGuard.NotNull(resource); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Service - Update resource"); @@ -490,7 +490,7 @@ public virtual async Task SetRelationshipAsync(TId leftId, string relationshipNa rightValue }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); + ArgumentGuard.NotNullNorEmpty(relationshipName); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Service - Set relationship"); @@ -562,8 +562,8 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TId leftId, string r rightResourceIds }); - ArgumentGuard.NotNullNorEmpty(relationshipName, nameof(relationshipName)); - ArgumentGuard.NotNull(rightResourceIds, nameof(rightResourceIds)); + ArgumentGuard.NotNullNorEmpty(relationshipName); + ArgumentGuard.NotNull(rightResourceIds); using IDisposable _ = CodeTimingSessionManager.Current.Measure("Repository - Remove from to-many relationship"); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs index fdf91e9744..41aa048ecb 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs @@ -15,7 +15,7 @@ public sealed class MusicTrackReleaseDefinition : JsonApiResourceDefinition : JsonApiRes protected HitCountingResourceDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) : base(resourceGraph) { - ArgumentGuard.NotNull(hitCounter, nameof(hitCounter)); + ArgumentGuard.NotNull(hitCounter); _hitCounter = hitCounter; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs index 6516100641..faa496aef9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs @@ -16,7 +16,7 @@ public sealed class InjectionDbContext : DbContext public InjectionDbContext(DbContextOptions options, ISystemClock systemClock) : base(options) { - ArgumentGuard.NotNull(systemClock, nameof(systemClock)); + ArgumentGuard.NotNull(systemClock); SystemClock = systemClock; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs index 6aea10c7ea..ed1a69569c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionFakers.cs @@ -20,7 +20,7 @@ internal sealed class InjectionFakers : FakerContainer public InjectionFakers(IServiceProvider serviceProvider) { - ArgumentGuard.NotNull(serviceProvider, nameof(serviceProvider)); + ArgumentGuard.NotNull(serviceProvider); _serviceProvider = serviceProvider; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceTypeCapturingDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceTypeCapturingDefinition.cs index da32a75e22..3b92b278da 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceTypeCapturingDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceTypeCapturingDefinition.cs @@ -20,8 +20,8 @@ public sealed class ResourceTypeCapturingDefinition : JsonApiRes public ResourceTypeCapturingDefinition(IResourceGraph resourceGraph, IJsonApiRequest request, ResourceTypeCaptureStore captureStore) : base(resourceGraph) { - ArgumentGuard.NotNull(request, nameof(request)); - ArgumentGuard.NotNull(captureStore, nameof(captureStore)); + ArgumentGuard.NotNull(request); + ArgumentGuard.NotNull(captureStore); _request = request; _captureStore = captureStore; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/WheelSortDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/WheelSortDefinition.cs index e5ae558489..e66e05bc64 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/WheelSortDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/WheelSortDefinition.cs @@ -19,7 +19,7 @@ public sealed class WheelSortDefinition : JsonApiResourceDefinition public WheelSortDefinition(IResourceGraph resourceGraph, IHttpContextAccessor httpContextAccessor) : base(resourceGraph) { - ArgumentGuard.NotNull(httpContextAccessor, nameof(httpContextAccessor)); + ArgumentGuard.NotNull(httpContextAccessor); _httpContextAccessor = httpContextAccessor; } diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs index 785c2ebd5c..d7d6484d2a 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs @@ -94,7 +94,7 @@ private sealed class SomeSingletonService // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local public SomeSingletonService(SomeScopedService scopedService) { - ArgumentGuard.NotNull(scopedService, nameof(scopedService)); + ArgumentGuard.NotNull(scopedService); } } @@ -109,7 +109,7 @@ private sealed class CircularServiceA // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local public CircularServiceA(CircularServiceB serviceB) { - ArgumentGuard.NotNull(serviceB, nameof(serviceB)); + ArgumentGuard.NotNull(serviceB); } } @@ -119,7 +119,7 @@ private sealed class CircularServiceB // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local public CircularServiceB(CircularServiceA serviceA) { - ArgumentGuard.NotNull(serviceA, nameof(serviceA)); + ArgumentGuard.NotNull(serviceA); } } diff --git a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs index 51430c92f8..2a11397f69 100644 --- a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs +++ b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ internal static class ServiceCollectionExtensions { public static void ReplaceControllers(this IServiceCollection services, TestControllerProvider provider) { - ArgumentGuard.NotNull(services, nameof(services)); + ArgumentGuard.NotNull(services); services.AddMvcCore().ConfigureApplicationPartManager(manager => { diff --git a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs index d4ab3dc841..aca0d91db1 100644 --- a/test/UnitTests/Models/ResourceConstructionExpressionTests.cs +++ b/test/UnitTests/Models/ResourceConstructionExpressionTests.cs @@ -52,7 +52,7 @@ private sealed class ResourceWithStringConstructor : Identifiable public ResourceWithStringConstructor(string text) { - ArgumentGuard.NotNullNorEmpty(text, nameof(text)); + ArgumentGuard.NotNullNorEmpty(text); Text = text; } From a5217e6f010baede700cbf5f29f81e2c3f9e89f0 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 02:14:14 +0200 Subject: [PATCH 09/47] Remove end_of_line from .editorconfig to avoid dialog in Visual Studio (git handles this automatically if omitted) --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index b6d9a8990c..999c890fb1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,6 @@ indent_style = space indent_size = 4 charset = utf-8 trim_trailing_whitespace = true -end_of_line = lf insert_final_newline = true [*.{csproj,json}] From 4b683087c3ffb2640dabf8a4a3b61b3f04653994 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 02:53:45 +0200 Subject: [PATCH 10/47] Update .editorconfig to better align with Resharper settings --- .editorconfig | 61 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/.editorconfig b/.editorconfig index 999c890fb1..ca191cf90e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,14 +8,18 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[*.{csproj,json}] +[*.{config,csproj,css,js,json,props,ruleset,xslt}] indent_size = 2 [*.{cs}] -#### .NET Coding Conventions #### +#### C#/.NET Coding Conventions #### -# Organize usings +# 'using' directive preferences dotnet_sort_system_directives_first = true +csharp_using_directive_placement = outside_namespace:suggestion + +# Namespace declarations +csharp_style_namespace_declarations = file_scoped:suggestion # this. preferences dotnet_style_qualification_for_field = false:suggestion @@ -29,6 +33,7 @@ dotnet_style_predefined_type_for_member_access = true:suggestion # Modifier preferences dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion +csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion csharp_style_pattern_local_over_anonymous_function = false:silent # Expression-level preferences @@ -36,6 +41,7 @@ dotnet_style_operator_placement_when_wrapping = end_of_line dotnet_style_prefer_auto_properties = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_return = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion # Parameter preferences dotnet_code_quality_unused_parameters = non_public:suggestion @@ -53,38 +59,38 @@ csharp_style_expression_bodied_properties = true:suggestion # Code-block preferences csharp_prefer_braces = true:suggestion -# Expression-level preferences -csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion - -# 'using' directive preferences -csharp_using_directive_placement = outside_namespace:suggestion - - -#### C# Formatting Rules #### - # Indentation preferences csharp_indent_case_contents_when_block = false # Wrapping preferences csharp_preserve_single_line_statements = false +# 'var' usage preferences +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion -#### Naming styles #### +#### Naming Style #### dotnet_diagnostic.IDE1006.severity = warning # Naming rules -dotnet_naming_rule.private_const_fields_should_be_pascal_case.symbols = private_const_fields -dotnet_naming_rule.private_const_fields_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.private_const_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.const_fields_should_be_pascal_case.symbols = const_fields +dotnet_naming_rule.const_fields_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.const_fields_should_be_pascal_case.severity = warning dotnet_naming_rule.private_static_readonly_fields_should_be_pascal_case.symbols = private_static_readonly_fields dotnet_naming_rule.private_static_readonly_fields_should_be_pascal_case.style = pascal_case dotnet_naming_rule.private_static_readonly_fields_should_be_pascal_case.severity = warning -dotnet_naming_rule.private_static_or_readonly_fields_should_start_with_underscore.symbols = private_static_or_readonly_fields -dotnet_naming_rule.private_static_or_readonly_fields_should_start_with_underscore.style = camel_case_prefix_with_underscore -dotnet_naming_rule.private_static_or_readonly_fields_should_start_with_underscore.severity = warning +dotnet_naming_rule.private_fields_should_start_with_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_should_start_with_underscore.style = camel_case_prefix_with_underscore +dotnet_naming_rule.private_fields_should_start_with_underscore.severity = warning dotnet_naming_rule.locals_and_parameters_should_be_camel_case.symbols = locals_and_parameters dotnet_naming_rule.locals_and_parameters_should_be_camel_case.style = camel_case @@ -95,25 +101,24 @@ dotnet_naming_rule.types_and_members_should_be_pascal_case.style = pascal_case dotnet_naming_rule.types_and_members_should_be_pascal_case.severity = warning # Symbol specifications -dotnet_naming_symbols.private_const_fields.applicable_kinds = field -dotnet_naming_symbols.private_const_fields.applicable_accessibilities = private -dotnet_naming_symbols.private_const_fields.required_modifiers = const +dotnet_naming_symbols.const_fields.applicable_kinds = field +dotnet_naming_symbols.const_fields.applicable_accessibilities = * +dotnet_naming_symbols.const_fields.required_modifiers = const dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private -dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static,readonly +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly -dotnet_naming_symbols.private_static_or_readonly_fields.applicable_kinds = field -dotnet_naming_symbols.private_static_or_readonly_fields.applicable_accessibilities = private -dotnet_naming_symbols.private_static_or_readonly_fields.required_modifiers = static readonly +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private -dotnet_naming_symbols.locals_and_parameters.applicable_kinds = local,parameter +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = local, parameter dotnet_naming_symbols.locals_and_parameters.applicable_accessibilities = * dotnet_naming_symbols.types_and_members.applicable_kinds = * dotnet_naming_symbols.types_and_members.applicable_accessibilities = * -# Naming styles +# Style specifications dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.camel_case_prefix_with_underscore.required_prefix = _ From 2aae8446f4af51da8ed27c7545ef16a5ee37ba16 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 24 Sep 2022 04:16:27 +0200 Subject: [PATCH 11/47] Include ruleset in solution, so it gets reformatted --- JsonApiDotNetCore.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/JsonApiDotNetCore.sln b/JsonApiDotNetCore.sln index 21b1cca7ce..043377403f 100644 --- a/JsonApiDotNetCore.sln +++ b/JsonApiDotNetCore.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore + CodingGuidelines.ruleset = CodingGuidelines.ruleset CSharpGuidelinesAnalyzer.config = CSharpGuidelinesAnalyzer.config Directory.Build.props = Directory.Build.props EndProjectSection From 8008e5e914e47492f180bea38be5351b217311c4 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 28 Sep 2022 04:07:59 +0200 Subject: [PATCH 12/47] Refresh .gitignore from source, preserving our additions --- .gitignore | 58 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 2bd200a72d..5fad128e2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -90,6 +90,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -97,9 +98,6 @@ StyleCopReport.xml *.svclog *.scc -# MacOS file systems -**/.DS_STORE - # Chutzpah Test files _Chutzpah* @@ -134,9 +132,6 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JetBrains Rider -.idea/ - # TeamCity is a build add-in _TeamCity* @@ -148,7 +143,9 @@ _TeamCity* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool -coverage*[.json, .xml, .info] +coverage*.json +coverage*.xml +coverage*.info # Visual Studio code coverage results *.coverage @@ -297,6 +294,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -353,6 +361,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -365,5 +376,36 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +############################################# +### Additions specific to this repository ### +############################################# + +# MacOS file systems +**/.DS_STORE + # Sqlite example databases *.db + +# JetBrains Rider +.idea/ From 292fb45c160327009028df84cb730656a7612d7b Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 28 Sep 2022 04:21:42 +0200 Subject: [PATCH 13/47] Add Rider-specific formatter settings --- .gitignore | 18 +++++++++++++++-- .../.idea.JsonApiDotNetCore/.idea/.gitignore | 1 + .../.idea/codeStyles/Project.xml | 20 +++++++++++++++++++ .../.idea/codeStyles/codeStyleConfig.xml | 5 +++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .idea/.idea.JsonApiDotNetCore/.idea/.gitignore create mode 100644 .idea/.idea.JsonApiDotNetCore/.idea/codeStyles/Project.xml create mode 100644 .idea/.idea.JsonApiDotNetCore/.idea/codeStyles/codeStyleConfig.xml diff --git a/.gitignore b/.gitignore index 5fad128e2d..85bd0f1080 100644 --- a/.gitignore +++ b/.gitignore @@ -407,5 +407,19 @@ FodyWeavers.xsd # Sqlite example databases *.db -# JetBrains Rider -.idea/ +# JetBrains IDEs Rider/IntelliJ (based on https://intellij-support.jetbrains.com/hc/en-us/articles/206544839) +**/.idea/**/*.xml +**/.idea/**/*.iml +**/.idea/**/*.ids +**/.idea/**/*.ipr +**/.idea/**/*.iws +**/.idea/**/*.name +**/.idea/**/*.properties +**/.idea/**/*.ser +**/.idea/**/shelf/ +**/.idea/**/dictionaries/ +**/.idea/**/libraries/ +**/.idea/**/artifacts/ +**/.idea/**/httpRequests/ +**/.idea/**/dataSources/ +!**/.idea/**/codeStyles/* diff --git a/.idea/.idea.JsonApiDotNetCore/.idea/.gitignore b/.idea/.idea.JsonApiDotNetCore/.idea/.gitignore new file mode 100644 index 0000000000..3933e947a2 --- /dev/null +++ b/.idea/.idea.JsonApiDotNetCore/.idea/.gitignore @@ -0,0 +1 @@ +# Empty .gitignore file to prevent Rider from adding one diff --git a/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/Project.xml b/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..902b9f865f --- /dev/null +++ b/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/Project.xml @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/codeStyleConfig.xml b/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..405cd65360 --- /dev/null +++ b/.idea/.idea.JsonApiDotNetCore/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + From 6c11baed406f07e213ffafc0d57a7eeb1ef78a01 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 30 Sep 2022 18:33:15 +0200 Subject: [PATCH 14/47] Add missing CDATA sections to code fragments --- .../Resources/Annotations/AttrAttribute.cs | 4 ++-- .../TypeWithAttributeSyntaxReceiver.cs | 3 +-- .../Configuration/IJsonApiOptions.cs | 12 +++++++----- src/JsonApiDotNetCore/Configuration/TypeLocator.cs | 4 ++-- .../JsonConverters/WriteOnlyDocumentConverter.cs | 2 +- .../WriteOnlyRelationshipObjectConverter.cs | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs index d3d6133f6e..448d6e8ab2 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs @@ -18,13 +18,13 @@ public sealed class AttrAttribute : ResourceFieldAttribute /// is used. /// /// - /// + /// + /// ]]> /// public AttrCapabilities Capabilities { diff --git a/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs index b23de19cc9..ad7b0d6ad5 100644 --- a/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs +++ b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs @@ -22,8 +22,7 @@ namespace JsonApiDotNetCore.SourceGenerators; /// /// [AlternateTypeName] /// public class ExampleResource4 : Identifiable { } -/// ]]> -/// +/// ]]> /// internal sealed class TypeWithAttributeSyntaxReceiver : ISyntaxReceiver { diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index 597d22294d..3c32594ea8 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -14,7 +14,9 @@ public interface IJsonApiOptions /// The URL prefix to use for exposed endpoints. /// /// - /// options.Namespace = "api/v1"; + /// /// string? Namespace { get; } @@ -42,10 +44,10 @@ public interface IJsonApiOptions /// Use relative links for all resources. False by default. /// /// - /// + /// - /// + /// ]]> + /// + /// ]]> /// bool UseRelativeLinks { get; } diff --git a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs index e4b1da8d01..981c50c4e4 100644 --- a/src/JsonApiDotNetCore/Configuration/TypeLocator.cs +++ b/src/JsonApiDotNetCore/Configuration/TypeLocator.cs @@ -147,9 +147,9 @@ public IReadOnlyCollection GetDerivedTypesForUnboundType(Assembly assembly /// The inherited type. /// /// - /// + /// + /// ]]> /// public IEnumerable GetDerivedTypes(Assembly assembly, Type baseType) { diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyDocumentConverter.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyDocumentConverter.cs index 623857f5ff..a48b8bd4be 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyDocumentConverter.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyDocumentConverter.cs @@ -25,7 +25,7 @@ public override Document Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } /// - /// Conditionally writes "data": null or omits it, depending on . + /// Conditionally writes or omits it, depending on . /// public override void Write(Utf8JsonWriter writer, Document value, JsonSerializerOptions options) { diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyRelationshipObjectConverter.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyRelationshipObjectConverter.cs index 047e0737c5..b740642868 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyRelationshipObjectConverter.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/WriteOnlyRelationshipObjectConverter.cs @@ -20,7 +20,7 @@ public override RelationshipObject Read(ref Utf8JsonReader reader, Type typeToCo } /// - /// Conditionally writes "data": null or omits it, depending on . + /// Conditionally writes or omits it, depending on . /// public override void Write(Utf8JsonWriter writer, RelationshipObject value, JsonSerializerOptions options) { From 78b1f511a35d2ed4b28c8483a38fa128f671db77 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 1 Oct 2022 16:18:29 +0200 Subject: [PATCH 15/47] Add capabilities for relationships --- .../extensibility/resource-definitions.md | 4 +- docs/usage/resources/attributes.md | 43 ++++--- docs/usage/resources/relationships.md | 106 +++++++++++++++++- .../Models/TodoItem.cs | 4 +- .../Resources/Annotations/AttrAttribute.cs | 7 +- .../Annotations/AttrCapabilities.shared.cs | 19 ++-- .../Resources/Annotations/HasManyAttribute.cs | 45 ++++++++ .../HasManyAttribute.netstandard.cs | 2 + .../Annotations/HasManyCapabilities.shared.cs | 51 +++++++++ .../Resources/Annotations/HasOneAttribute.cs | 45 ++++++++ .../HasOneAttribute.netstandard.cs | 2 + .../Annotations/HasOneCapabilities.shared.cs | 35 ++++++ .../Annotations/RelationshipAttribute.cs | 27 +++-- .../RelationshipAttribute.netstandard.cs | 1 + .../Annotations/ResourceFieldAttribute.cs | 2 +- .../Configuration/IJsonApiOptions.cs | 12 +- .../Configuration/JsonApiOptions.cs | 6 + .../Configuration/ResourceGraphBuilder.cs | 47 ++++++++ .../Queries/Internal/Parsing/IncludeParser.cs | 4 +- .../Queries/Internal/SparseFieldSetCache.cs | 6 +- .../FilterQueryStringParameterReader.cs | 8 +- ...parseFieldSetQueryStringParameterReader.cs | 8 +- .../JsonApiQueryStringParameters.cs | 8 +- .../Annotations/CapabilitiesExtensions.cs | 45 ++++++++ .../Adapters/AtomicReferenceAdapter.cs | 1 + ...tInResourceOrRelationshipRequestAdapter.cs | 1 + .../Adapters/ResourceIdentityAdapter.cs | 49 ++++++++ .../Request/Adapters/ResourceObjectAdapter.cs | 9 +- .../Response/ResponseModelAdapter.cs | 7 ++ test/AnnotationTests/Models/TreeNode.cs | 4 +- .../Creating/AtomicCreateResourceTests.cs | 84 +++++++------- ...eateResourceWithToManyRelationshipTests.cs | 52 +++++++++ ...reateResourceWithToOneRelationshipTests.cs | 49 ++++++++ .../AtomicOperations/Lyric.cs | 2 +- .../AtomicOperations/MusicTrack.cs | 2 +- .../AtomicAddToToManyRelationshipTests.cs | 53 +++++++++ ...AtomicRemoveFromToManyRelationshipTests.cs | 53 +++++++++ .../AtomicReplaceToManyRelationshipTests.cs | 53 +++++++++ .../AtomicUpdateToOneRelationshipTests.cs | 53 +++++++++ .../AtomicReplaceToManyRelationshipTests.cs | 61 ++++++++++ .../Resources/AtomicUpdateResourceTests.cs | 102 ++++++++--------- .../AtomicUpdateToOneRelationshipTests.cs | 58 ++++++++++ .../IntegrationTests/QueryStrings/BlogPost.cs | 2 + .../IntegrationTests/QueryStrings/Calendar.cs | 5 +- .../QueryStrings/Filtering/FilterTests.cs | 23 ++++ .../QueryStrings/Includes/IncludeTests.cs | 84 +++++++++++++- .../SerializerIgnoreConditionTests.cs | 10 +- .../SparseFieldSets/SparseFieldSetTests.cs | 49 ++++++++ .../QueryStrings/WebAccount.cs | 4 +- .../ReadWrite/Creating/CreateResourceTests.cs | 70 ++++++------ ...eateResourceWithToManyRelationshipTests.cs | 46 ++++++++ ...reateResourceWithToOneRelationshipTests.cs | 42 +++++++ .../AddToToManyRelationshipTests.cs | 43 +++++++ .../RemoveFromToManyRelationshipTests.cs | 43 +++++++ .../ReplaceToManyRelationshipTests.cs | 43 +++++++ .../UpdateToOneRelationshipTests.cs | 39 +++++++ .../ReplaceToManyRelationshipTests.cs | 55 +++++++++ .../Updating/Resources/UpdateResourceTests.cs | 88 +++++++-------- .../Resources/UpdateToOneRelationshipTests.cs | 51 +++++++++ .../IntegrationTests/ReadWrite/WorkItem.cs | 4 +- .../ReadWrite/WorkItemGroup.cs | 2 +- 61 files changed, 1682 insertions(+), 251 deletions(-) create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyCapabilities.shared.cs create mode 100644 src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneCapabilities.shared.cs create mode 100644 src/JsonApiDotNetCore/Resources/Annotations/CapabilitiesExtensions.cs diff --git a/docs/usage/extensibility/resource-definitions.md b/docs/usage/extensibility/resource-definitions.md index af4f8a27c5..6bc16b869e 100644 --- a/docs/usage/extensibility/resource-definitions.md +++ b/docs/usage/extensibility/resource-definitions.md @@ -34,10 +34,10 @@ from Entity Framework Core `IQueryable` execution. ### Excluding fields -There are some cases where you want attributes (or relationships) conditionally excluded from your resource response. +There are some cases where you want attributes or relationships conditionally excluded from your resource response. For example, you may accept some sensitive data that should only be exposed to administrators after creation. -**Note:** to exclude attributes unconditionally, use `[Attr(Capabilities = ~AttrCapabilities.AllowView)]` on a resource class property. +**Note:** to exclude fields unconditionally, [attribute capabilities](~/usage/resources/attributes.md#capabilities) and [relationship capabilities](~/usage/resources/relationships.md#capabilities) can be used instead. ```c# public class UserDefinition : JsonApiResourceDefinition diff --git a/docs/usage/resources/attributes.md b/docs/usage/resources/attributes.md index 669dba0892..77c6ff9566 100644 --- a/docs/usage/resources/attributes.md +++ b/docs/usage/resources/attributes.md @@ -43,9 +43,10 @@ options.DefaultAttrCapabilities = AttrCapabilities.None; // default: All This can be overridden per attribute. -### Viewability +### AllowView -Attributes can be marked to allow returning their value in responses. When not allowed and requested using `?fields[]=`, it results in an HTTP 400 response. +Indicates whether the attribute value can be returned in responses. When not allowed and requested using `?fields[]=`, it results in an HTTP 400 response. +Otherwise, the attribute is silently omitted. ```c# #nullable enable @@ -57,45 +58,59 @@ public class User : Identifiable } ``` -### Creatability +### AllowFilter -Attributes can be marked as creatable, which will allow `POST` requests to assign a value to them. When sent but not allowed, an HTTP 422 response is returned. +Indicates whether the attribute can be filtered on. When not allowed and used in `?filter=`, an HTTP 400 is returned. ```c# #nullable enable public class Person : Identifiable { - [Attr(Capabilities = AttrCapabilities.AllowCreate)] - public string? CreatorName { get; set; } + [Attr(Capabilities = AttrCapabilities.AllowFilter)] + public string? FirstName { get; set; } } ``` -### Changeability +### AllowSort -Attributes can be marked as changeable, which will allow `PATCH` requests to update them. When sent but not allowed, an HTTP 422 response is returned. +Indicates whether the attribute can be sorted on. When not allowed and used in `?sort=`, an HTTP 400 is returned. ```c# #nullable enable public class Person : Identifiable { - [Attr(Capabilities = AttrCapabilities.AllowChange)] - public string? FirstName { get; set; }; + [Attr(Capabilities = ~AttrCapabilities.AllowSort)] + public string? FirstName { get; set; } } ``` -### Filter/Sort-ability +### AllowCreate -Attributes can be marked to allow filtering and/or sorting. When not allowed, it results in an HTTP 400 response. +Indicates whether POST requests can assign the attribute value. When sent but not allowed, an HTTP 422 response is returned. ```c# #nullable enable public class Person : Identifiable { - [Attr(Capabilities = AttrCapabilities.AllowSort | AttrCapabilities.AllowFilter)] - public string? FirstName { get; set; } + [Attr(Capabilities = AttrCapabilities.AllowCreate)] + public string? CreatorName { get; set; } +} +``` + +### AllowChange + +Indicates whether PATCH requests can update the attribute value. When sent but not allowed, an HTTP 422 response is returned. + +```c# +#nullable enable + +public class Person : Identifiable +{ + [Attr(Capabilities = AttrCapabilities.AllowChange)] + public string? FirstName { get; set; }; } ``` diff --git a/docs/usage/resources/relationships.md b/docs/usage/resources/relationships.md index 8776041e98..14ff2eb7f5 100644 --- a/docs/usage/resources/relationships.md +++ b/docs/usage/resources/relationships.md @@ -160,7 +160,111 @@ public class TodoItem : Identifiable } ``` -## Includibility +## Capabilities + +_since v5.1_ + +Default JSON:API relationship capabilities are specified in +@JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_DefaultHasOneCapabilities and +@JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_DefaultHasManyCapabilities: + +```c# +options.DefaultHasOneCapabilities = HasOneCapabilities.None; // default: All +options.DefaultHasManyCapabilities = HasManyCapabilities.None; // default: All +``` + +This can be overridden per relationship. + +### AllowView + +Indicates whether the relationship can be returned in responses. When not allowed and requested using `?fields[]=`, it results in an HTTP 400 response. +Otherwise, the relationship (and its related resources, when included) are silently omitted. + +Note this setting does not affect retrieving the related resources directly. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasOne(Capabilities = ~HasOneCapabilities.AllowView)] + public LoginAccount Account { get; set; } = null!; +} +``` + +### AllowInclude + +Indicates whether the relationship can be included. When not allowed and used in `?include=`, an HTTP 400 is returned. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasMany(Capabilities = ~HasManyCapabilities.AllowInclude)] + public ISet Groups { get; set; } = new HashSet(); +} +``` + +### AllowFilter + +For to-many relationships only. Indicates whether it can be used in the `count()` and `has()` filter functions. When not allowed and used in `?filter=`, an HTTP 400 is returned. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasMany(Capabilities = HasManyCapabilities.AllowFilter)] + public ISet Groups { get; set; } = new HashSet(); +} +``` + +### AllowSet + +Indicates whether POST and PATCH requests can replace the relationship. When sent but not allowed, an HTTP 422 response is returned. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasOne(Capabilities = ~HasOneCapabilities.AllowSet)] + public LoginAccount Account { get; set; } = null!; +} +``` + +### AllowAdd + +For to-many relationships only. Indicates whether POST requests can add resources to the relationship. When sent but not allowed, an HTTP 422 response is returned. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasMany(Capabilities = ~HasManyCapabilities.AllowAdd)] + public ISet Groups { get; set; } = new HashSet(); +} +``` + +### AllowRemove + +For to-many relationships only. Indicates whether DELETE requests can remove resources from the relationship. When sent but not allowed, an HTTP 422 response is returned. + +```c# +#nullable enable + +public class User : Identifiable +{ + [HasMany(Capabilities = ~HasManyCapabilities.AllowRemove)] + public ISet Groups { get; set; } = new HashSet(); +} +``` + +## CanInclude + +_obsolete since v5.1_ Relationships can be marked to disallow including them using the `?include=` query string parameter. When not allowed, it results in an HTTP 400 response. diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index 9be7e6e64e..5fe508f7f2 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -25,9 +25,9 @@ public sealed class TodoItem : Identifiable [HasOne] public Person Owner { get; set; } = null!; - [HasOne] + [HasOne(Capabilities = HasOneCapabilities.AllowView | HasOneCapabilities.AllowSet)] public Person? Assignee { get; set; } - [HasMany] + [HasMany(Capabilities = HasManyCapabilities.AllowView | HasManyCapabilities.AllowFilter)] public ISet Tags { get; set; } = new HashSet(); } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs index 448d6e8ab2..7a6cbd960f 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs @@ -14,15 +14,14 @@ public sealed class AttrAttribute : ResourceFieldAttribute internal bool HasExplicitCapabilities => _capabilities != null; /// - /// The set of capabilities that are allowed to be performed on this attribute. When not explicitly assigned, the configured default set of capabilities - /// is used. + /// The set of allowed capabilities on this attribute. When not explicitly set, the configured default set of capabilities is used. /// /// /// /// { /// [Attr(Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort)] - /// public string Name { get; set; } + /// public string Name { get; set; } = null!; /// } /// ]]> /// diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs index 2812be6d39..0951010b3b 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrCapabilities.shared.cs @@ -3,7 +3,7 @@ namespace JsonApiDotNetCore.Resources.Annotations; /// -/// Indicates capabilities that can be performed on an . +/// Indicates what can be performed on an . /// [PublicAPI] [Flags] @@ -12,29 +12,32 @@ public enum AttrCapabilities None = 0, /// - /// Whether or not GET requests can retrieve the attribute. Attempts to retrieve when disabled will return an HTTP 400 response. + /// Whether or not the attribute value can be returned in responses. Attempts to explicitly request it via the fields query string parameter when + /// disabled will return an HTTP 400 response. Otherwise, the attribute is silently omitted. /// AllowView = 1, /// /// Whether or not POST requests can assign the attribute value. Attempts to assign when disabled will return an HTTP 422 response. /// - AllowCreate = 2, + AllowCreate = 1 << 1, /// /// Whether or not PATCH requests can update the attribute value. Attempts to update when disabled will return an HTTP 422 response. /// - AllowChange = 4, + AllowChange = 1 << 2, /// - /// Whether or not an attribute can be filtered on via a query string parameter. Attempts to filter when disabled will return an HTTP 400 response. + /// Whether or not the attribute can be filtered on. Attempts to use it in the filter query string parameter when disabled will return an HTTP 400 + /// response. /// - AllowFilter = 8, + AllowFilter = 1 << 3, /// - /// Whether or not an attribute can be sorted on via a query string parameter. Attempts to sort when disabled will return an HTTP 400 response. + /// Whether or not the attribute can be sorted on. Attempts to use it in the sort query string parameter when disabled will return an HTTP 400 + /// response. /// - AllowSort = 16, + AllowSort = 1 << 4, All = AllowView | AllowCreate | AllowChange | AllowFilter | AllowSort } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs index 39bcf34b3f..5792744d5c 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs @@ -1,5 +1,7 @@ using JetBrains.Annotations; +// ReSharper disable NonReadonlyMemberInGetHashCode + namespace JsonApiDotNetCore.Resources.Annotations; /// @@ -20,12 +22,33 @@ namespace JsonApiDotNetCore.Resources.Annotations; public sealed class HasManyAttribute : RelationshipAttribute { private readonly Lazy _lazyIsManyToMany; + private HasManyCapabilities? _capabilities; /// /// Inspects to determine if this is a many-to-many relationship. /// internal bool IsManyToMany => _lazyIsManyToMany.Value; + internal bool HasExplicitCapabilities => _capabilities != null; + + /// + /// The set of allowed capabilities on this to-many relationship. When not explicitly set, the configured default set of capabilities is used. + /// + /// + /// + /// { + /// [HasMany(Capabilities = HasManyCapabilities.AllowView | HasManyCapabilities.AllowInclude)] + /// public ISet Chapters { get; set; } = new HashSet(); + /// } + /// ]]> + /// + public HasManyCapabilities Capabilities + { + get => _capabilities ?? default; + set => _capabilities = value; + } + public HasManyAttribute() { _lazyIsManyToMany = new Lazy(EvaluateIsManyToMany, LazyThreadSafetyMode.PublicationOnly); @@ -41,4 +64,26 @@ private bool EvaluateIsManyToMany() return false; } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is null || GetType() != obj.GetType()) + { + return false; + } + + var other = (HasManyAttribute)obj; + + return _capabilities == other._capabilities && base.Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_capabilities, base.GetHashCode()); + } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs index 1cdeb9f62f..cf83f0ce17 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.netstandard.cs @@ -9,4 +9,6 @@ namespace JsonApiDotNetCore.Resources.Annotations; [AttributeUsage(AttributeTargets.Property)] public sealed class HasManyAttribute : RelationshipAttribute { + /// + public HasManyCapabilities Capabilities { get; set; } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyCapabilities.shared.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyCapabilities.shared.cs new file mode 100644 index 0000000000..cf65951321 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyCapabilities.shared.cs @@ -0,0 +1,51 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// Indicates what can be performed on a . +/// +[PublicAPI] +[Flags] +public enum HasManyCapabilities +{ + None = 0, + + /// + /// Whether or not the relationship can be returned in responses. Attempts to explicitly request it via the fields query string parameter when + /// disabled will return an HTTP 400 response. Otherwise, the relationship (and its related resources, when included) are silently omitted. + /// + /// + /// Note this setting does not affect retrieving the related resources directly. + /// + AllowView = 1, + + /// + /// Whether or not the relationship can be included. Attempts to use it in the include query string parameter when disabled will return an HTTP + /// 400 response. + /// + AllowInclude = 1 << 1, + + /// + /// Whether or not the to-many relationship can be used in the count() and has() functions as part of the filter query string + /// parameter. Attempts to use it when disabled will return an HTTP 400 response. + /// + AllowFilter = 1 << 2, + + /// + /// Whether or not POST and PATCH requests can replace the relationship. Attempts to replace when disabled will return an HTTP 422 response. + /// + AllowSet = 1 << 3, + + /// + /// Whether or not POST requests can add to the to-many relationship. Attempts to add when disabled will return an HTTP 422 response. + /// + AllowAdd = 1 << 4, + + /// + /// Whether or not DELETE requests can remove from the to-many relationship. Attempts to remove when disabled will return an HTTP 422 response. + /// + AllowRemove = 1 << 5, + + All = AllowView | AllowInclude | AllowFilter | AllowSet | AllowAdd | AllowRemove +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs index 0a68f702d3..c0416c92fb 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs @@ -1,5 +1,7 @@ using JetBrains.Annotations; +// ReSharper disable NonReadonlyMemberInGetHashCode + namespace JsonApiDotNetCore.Resources.Annotations; /// @@ -19,12 +21,33 @@ namespace JsonApiDotNetCore.Resources.Annotations; public sealed class HasOneAttribute : RelationshipAttribute { private readonly Lazy _lazyIsOneToOne; + private HasOneCapabilities? _capabilities; /// /// Inspects to determine if this is a one-to-one relationship. /// internal bool IsOneToOne => _lazyIsOneToOne.Value; + internal bool HasExplicitCapabilities => _capabilities != null; + + /// + /// The set of allowed capabilities on this to-one relationship. When not explicitly set, the configured default set of capabilities is used. + /// + /// + /// + /// { + /// [HasOne(Capabilities = HasOneCapabilities.AllowView | HasOneCapabilities.AllowInclude)] + /// public Person? Author { get; set; } + /// } + /// ]]> + /// + public HasOneCapabilities Capabilities + { + get => _capabilities ?? default; + set => _capabilities = value; + } + public HasOneAttribute() { _lazyIsOneToOne = new Lazy(EvaluateIsOneToOne, LazyThreadSafetyMode.PublicationOnly); @@ -40,4 +63,26 @@ private bool EvaluateIsOneToOne() return false; } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is null || GetType() != obj.GetType()) + { + return false; + } + + var other = (HasOneAttribute)obj; + + return _capabilities == other._capabilities && base.Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_capabilities, base.GetHashCode()); + } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs index 1c16fb01b2..42be2f3c5f 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.netstandard.cs @@ -9,4 +9,6 @@ namespace JsonApiDotNetCore.Resources.Annotations; [AttributeUsage(AttributeTargets.Property)] public sealed class HasOneAttribute : RelationshipAttribute { + /// + public HasOneCapabilities Capabilities { get; set; } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneCapabilities.shared.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneCapabilities.shared.cs new file mode 100644 index 0000000000..a001e39407 --- /dev/null +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneCapabilities.shared.cs @@ -0,0 +1,35 @@ +using JetBrains.Annotations; + +namespace JsonApiDotNetCore.Resources.Annotations; + +/// +/// Indicates what can be performed on a . +/// +[PublicAPI] +[Flags] +public enum HasOneCapabilities +{ + None = 0, + + /// + /// Whether or not the relationship can be returned in responses. Attempts to explicitly request it via the fields query string parameter when + /// disabled will return an HTTP 400 response. Otherwise, the relationship (and its related resources, when included) are silently omitted. + /// + /// + /// Note this setting does not affect retrieving the related resources directly. + /// + AllowView = 1, + + /// + /// Whether or not the relationship can be included. Attempts to use it in the include query string parameter when disabled will return an HTTP + /// 400 response. + /// + AllowInclude = 1 << 1, + + /// + /// Whether or not POST and PATCH requests can replace the relationship. Attempts to replace when disabled will return an HTTP 422 response. + /// + AllowSet = 1 << 2, + + All = AllowView | AllowInclude | AllowSet +} diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs index c1b24e9567..dd94bab221 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs @@ -14,9 +14,13 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute { private protected static readonly CollectionConverter CollectionConverter = new(); - // These are definitely assigned after building the resource graph, which is why their public equivalents are declared as non-nullable. + // This field is definitely assigned after building the resource graph, which is why its public equivalent is declared as non-nullable. private ResourceType? _rightType; + private bool? _canInclude; + + internal bool HasExplicitCanInclude => _canInclude != null; + /// /// The of the Entity Framework Core inverse navigation, which may or may not exist. Even if it exists, it may not be exposed /// as a JSON:API relationship. @@ -69,13 +73,18 @@ internal set public LinkTypes Links { get; set; } = LinkTypes.NotConfigured; /// - /// Whether or not this relationship can be included using the - /// - /// ?include=publicName - /// - /// query string parameter. This is true by default. + /// Whether or not this relationship can be included using the include query string parameter. This is true by default. /// - public bool CanInclude { get; set; } = true; + /// + /// When explicitly set, this value takes precedence over Capabilities for backwards-compatibility. Capabilities are adjusted accordingly when building + /// the resource graph. + /// + [Obsolete("Use AllowInclude in Capabilities instead.")] + public bool CanInclude + { + get => _canInclude ?? true; + set => _canInclude = value; + } public override bool Equals(object? obj) { @@ -91,11 +100,11 @@ public override bool Equals(object? obj) var other = (RelationshipAttribute)obj; - return _rightType?.ClrType == other._rightType?.ClrType && Links == other.Links && CanInclude == other.CanInclude && base.Equals(other); + return _rightType?.ClrType == other._rightType?.ClrType && Links == other.Links && base.Equals(other); } public override int GetHashCode() { - return HashCode.Combine(_rightType?.ClrType, Links, CanInclude, base.GetHashCode()); + return HashCode.Combine(_rightType?.ClrType, Links, base.GetHashCode()); } } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs index 51517b3d51..d7af592564 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.netstandard.cs @@ -12,5 +12,6 @@ public abstract class RelationshipAttribute : ResourceFieldAttribute public LinkTypes Links { get; set; } = LinkTypes.NotConfigured; /// + [Obsolete("Use AllowInclude in Capabilities instead.")] public bool CanInclude { get; set; } = true; } diff --git a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs index 9f32610dc9..e8e1d17aca 100644 --- a/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs +++ b/src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs @@ -19,7 +19,7 @@ public abstract class ResourceFieldAttribute : Attribute private ResourceType? _type; /// - /// The publicly exposed name of this JSON:API field. When not explicitly assigned, the configured naming convention is applied on the property name. + /// The publicly exposed name of this JSON:API field. When not explicitly set, the configured naming convention is applied on the property name. /// public string PublicName { diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index 3c32594ea8..bc7d17d89f 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -21,10 +21,20 @@ public interface IJsonApiOptions string? Namespace { get; } /// - /// Specifies the default query string capabilities that can be used on exposed JSON:API attributes. Defaults to . + /// Specifies the default set of allowed capabilities on JSON:API attributes. Defaults to . /// AttrCapabilities DefaultAttrCapabilities { get; } + /// + /// Specifies the default set of allowed capabilities on JSON:API to-one relationships. Defaults to . + /// + HasOneCapabilities DefaultHasOneCapabilities { get; } + + /// + /// Specifies the default set of allowed capabilities on JSON:API to-many relationships. Defaults to . + /// + HasManyCapabilities DefaultHasManyCapabilities { get; } + /// /// Indicates whether responses should contain a jsonapi object that contains the highest JSON:API version supported. False by default. /// diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index 46603260cd..778ded8d59 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -26,6 +26,12 @@ public sealed class JsonApiOptions : IJsonApiOptions /// public AttrCapabilities DefaultAttrCapabilities { get; set; } = AttrCapabilities.All; + /// + public HasOneCapabilities DefaultHasOneCapabilities { get; set; } = HasOneCapabilities.All; + + /// + public HasManyCapabilities DefaultHasManyCapabilities { get; set; } = HasManyCapabilities.All; + /// public bool IncludeJsonApiVersion { get; set; } diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs index 2af0e63caf..2b6f19acd3 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs @@ -307,6 +307,7 @@ private IReadOnlyCollection GetRelationships(Type resourc { relationship.Property = property; SetPublicName(relationship, property); + SetRelationshipCapabilities(relationship); IncludeField(relationshipsByName, relationship); } @@ -321,6 +322,52 @@ private void SetPublicName(ResourceFieldAttribute field, PropertyInfo property) field.PublicName ??= FormatPropertyName(property); } + private void SetRelationshipCapabilities(RelationshipAttribute relationship) + { +#pragma warning disable CS0618 // Type or member is obsolete + bool canInclude = relationship.CanInclude; +#pragma warning restore CS0618 // Type or member is obsolete + + if (relationship is HasOneAttribute hasOneRelationship) + { + SetHasOneRelationshipCapabilities(hasOneRelationship, canInclude); + } + else if (relationship is HasManyAttribute hasManyRelationship) + { + SetHasManyRelationshipCapabilities(hasManyRelationship, canInclude); + } + } + + private void SetHasOneRelationshipCapabilities(HasOneAttribute hasOneRelationship, bool canInclude) + { + if (!hasOneRelationship.HasExplicitCapabilities) + { + hasOneRelationship.Capabilities = _options.DefaultHasOneCapabilities; + } + + if (hasOneRelationship.HasExplicitCanInclude) + { + hasOneRelationship.Capabilities = canInclude + ? hasOneRelationship.Capabilities | HasOneCapabilities.AllowInclude + : hasOneRelationship.Capabilities & ~HasOneCapabilities.AllowInclude; + } + } + + private void SetHasManyRelationshipCapabilities(HasManyAttribute hasManyRelationship, bool canInclude) + { + if (!hasManyRelationship.HasExplicitCapabilities) + { + hasManyRelationship.Capabilities = _options.DefaultHasManyCapabilities; + } + + if (hasManyRelationship.HasExplicitCanInclude) + { + hasManyRelationship.Capabilities = canInclude + ? hasManyRelationship.Capabilities | HasManyCapabilities.AllowInclude + : hasManyRelationship.Capabilities & ~HasManyCapabilities.AllowInclude; + } + } + private IReadOnlyCollection GetEagerLoads(Type resourceClrType, int recursionDepth = 0) { AssertNoInfiniteRecursion(recursionDepth); diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs index 7418be160f..38ee1ffa83 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs @@ -106,7 +106,7 @@ private ICollection LookupRelationshipName(string relationshipN { relationshipsFound.AddRange(relationships); - RelationshipAttribute[] relationshipsToInclude = relationships.Where(relationship => relationship.CanInclude).ToArray(); + RelationshipAttribute[] relationshipsToInclude = relationships.Where(relationship => !relationship.IsIncludeBlocked()).ToArray(); ICollection affectedChildren = parent.EnsureChildren(relationshipsToInclude); children.AddRange(affectedChildren); } @@ -139,7 +139,7 @@ private static void AssertRelationshipsFound(ISet relatio private static void AssertAtLeastOneCanBeIncluded(ISet relationshipsFound, string relationshipName, ICollection parents) { - if (relationshipsFound.All(relationship => !relationship.CanInclude)) + if (relationshipsFound.All(relationship => relationship.IsIncludeBlocked())) { string parentPath = parents.First().Path; ResourceType resourceType = relationshipsFound.First().LeftType; diff --git a/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs b/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs index 495af2ebc1..ab1edf9f9e 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs @@ -148,13 +148,11 @@ private static IImmutableSet GetViewableFields(ResourceT { ImmutableHashSet.Builder fieldSetBuilder = ImmutableHashSet.CreateBuilder(); - foreach (AttrAttribute attribute in resourceType.Attributes.Where(attr => attr.Capabilities.HasFlag(AttrCapabilities.AllowView))) + foreach (ResourceFieldAttribute field in resourceType.Fields.Where(nextField => !nextField.IsViewBlocked())) { - fieldSetBuilder.Add(attribute); + fieldSetBuilder.Add(field); } - fieldSetBuilder.AddRange(resourceType.Relationships); - return fieldSetBuilder.ToImmutable(); } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs index 18167acbc0..60c7f6b75c 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs @@ -40,10 +40,12 @@ public FilterQueryStringParameterReader(IJsonApiRequest request, IResourceGraph protected void ValidateSingleField(ResourceFieldAttribute field, ResourceType resourceType, string path) { - if (field is AttrAttribute attribute && !attribute.Capabilities.HasFlag(AttrCapabilities.AllowFilter)) + if (field.IsFilterBlocked()) { - throw new InvalidQueryStringParameterException(_lastParameterName!, "Filtering on the requested attribute is not allowed.", - $"Filtering on attribute '{attribute.PublicName}' is not allowed."); + string kind = field is AttrAttribute ? "attribute" : "relationship"; + + throw new InvalidQueryStringParameterException(_lastParameterName!, $"Filtering on the requested {kind} is not allowed.", + $"Filtering on {kind} '{field.PublicName}' is not allowed."); } } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs index dadf153f21..fb4f665873 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs @@ -36,10 +36,12 @@ public SparseFieldSetQueryStringParameterReader(IJsonApiRequest request, IResour protected void ValidateSingleField(ResourceFieldAttribute field, ResourceType resourceType, string path) { - if (field is AttrAttribute attribute && !attribute.Capabilities.HasFlag(AttrCapabilities.AllowView)) + if (field.IsViewBlocked()) { - throw new InvalidQueryStringParameterException(_lastParameterName!, "Retrieving the requested attribute is not allowed.", - $"Retrieving the attribute '{attribute.PublicName}' is not allowed."); + string kind = field is AttrAttribute ? "attribute" : "relationship"; + + throw new InvalidQueryStringParameterException(_lastParameterName!, $"Retrieving the requested {kind} is not allowed.", + $"Retrieving the {kind} '{field.PublicName}' is not allowed."); } } diff --git a/src/JsonApiDotNetCore/QueryStrings/JsonApiQueryStringParameters.cs b/src/JsonApiDotNetCore/QueryStrings/JsonApiQueryStringParameters.cs index c1f59ceab6..e7c418d8a7 100644 --- a/src/JsonApiDotNetCore/QueryStrings/JsonApiQueryStringParameters.cs +++ b/src/JsonApiDotNetCore/QueryStrings/JsonApiQueryStringParameters.cs @@ -10,9 +10,9 @@ public enum JsonApiQueryStringParameters { None = 0, Filter = 1, - Sort = 2, - Include = 4, - Page = 8, - Fields = 16, + Sort = 1 << 1, + Include = 1 << 2, + Page = 1 << 3, + Fields = 1 << 4, All = Filter | Sort | Include | Page | Fields } diff --git a/src/JsonApiDotNetCore/Resources/Annotations/CapabilitiesExtensions.cs b/src/JsonApiDotNetCore/Resources/Annotations/CapabilitiesExtensions.cs new file mode 100644 index 0000000000..84352f2206 --- /dev/null +++ b/src/JsonApiDotNetCore/Resources/Annotations/CapabilitiesExtensions.cs @@ -0,0 +1,45 @@ +namespace JsonApiDotNetCore.Resources.Annotations; + +internal static class CapabilitiesExtensions +{ + public static bool IsViewBlocked(this ResourceFieldAttribute field) + { + return field switch + { + AttrAttribute attrAttribute => !attrAttribute.Capabilities.HasFlag(AttrCapabilities.AllowView), + HasOneAttribute hasOneRelationship => !hasOneRelationship.Capabilities.HasFlag(HasOneCapabilities.AllowView), + HasManyAttribute hasManyRelationship => !hasManyRelationship.Capabilities.HasFlag(HasManyCapabilities.AllowView), + _ => false + }; + } + + public static bool IsIncludeBlocked(this RelationshipAttribute relationship) + { + return relationship switch + { + HasOneAttribute hasOneRelationship => !hasOneRelationship.Capabilities.HasFlag(HasOneCapabilities.AllowInclude), + HasManyAttribute hasManyRelationship => !hasManyRelationship.Capabilities.HasFlag(HasManyCapabilities.AllowInclude), + _ => false + }; + } + + public static bool IsFilterBlocked(this ResourceFieldAttribute field) + { + return field switch + { + AttrAttribute attrAttribute => !attrAttribute.Capabilities.HasFlag(AttrCapabilities.AllowFilter), + HasManyAttribute hasManyRelationship => !hasManyRelationship.Capabilities.HasFlag(HasManyCapabilities.AllowFilter), + _ => false + }; + } + + public static bool IsSetBlocked(this RelationshipAttribute relationship) + { + return relationship switch + { + HasOneAttribute hasOneRelationship => !hasOneRelationship.Capabilities.HasFlag(HasOneCapabilities.AllowSet), + HasManyAttribute hasManyRelationship => !hasManyRelationship.Capabilities.HasFlag(HasManyCapabilities.AllowSet), + _ => false + }; + } +} diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceAdapter.cs index fd2c274dab..f7a5ad82fa 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicReferenceAdapter.cs @@ -39,6 +39,7 @@ private RelationshipAttribute ConvertRelationship(string relationshipName, Resou AssertIsKnownRelationship(relationship, relationshipName, resourceType, state); AssertToManyInAddOrRemoveRelationship(relationship, state); + AssertRelationshipChangeNotBlocked(relationship, state); return relationship; } diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs index f5d4cb088c..38a8ce0a29 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/DocumentInResourceOrRelationshipRequestAdapter.cs @@ -48,6 +48,7 @@ public DocumentInResourceOrRelationshipRequestAdapter(IJsonApiOptions options, I } ResourceIdentityAdapter.AssertToManyInAddOrRemoveRelationship(state.Request.Relationship, state); + ResourceIdentityAdapter.AssertRelationshipChangeNotBlocked(state.Request.Relationship, state); state.WritableTargetedFields.Relationships.Add(state.Request.Relationship); return _relationshipDataAdapter.Convert(document.Data, state.Request.Relationship, false, state); diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs index 9df5215da9..e4c0df21df 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs @@ -231,4 +231,53 @@ protected internal static void AssertToManyInAddOrRemoveRelationship(Relationshi HttpStatusCode.Forbidden); } } + + internal static void AssertRelationshipChangeNotBlocked(RelationshipAttribute relationship, RequestAdapterState state) + { + switch (state.Request.WriteOperation) + { + case WriteOperationKind.AddToRelationship: + { + AssertAddToRelationshipNotBlocked((HasManyAttribute)relationship, state); + break; + } + case WriteOperationKind.RemoveFromRelationship: + { + AssertRemoveFromRelationshipNotBlocked((HasManyAttribute)relationship, state); + break; + } + default: + { + AssertSetRelationshipNotBlocked(relationship, state); + break; + } + } + } + + private static void AssertSetRelationshipNotBlocked(RelationshipAttribute relationship, RequestAdapterState state) + { + if (relationship.IsSetBlocked()) + { + throw new ModelConversionException(state.Position, "Relationship cannot be assigned.", + $"The relationship '{relationship.PublicName}' on resource type '{relationship.LeftType.PublicName}' cannot be assigned to."); + } + } + + private static void AssertAddToRelationshipNotBlocked(HasManyAttribute relationship, RequestAdapterState state) + { + if (!relationship.Capabilities.HasFlag(HasManyCapabilities.AllowAdd)) + { + throw new ModelConversionException(state.Position, "Relationship cannot be added to.", + $"The relationship '{relationship.PublicName}' on resource type '{relationship.LeftType.PublicName}' cannot be added to."); + } + } + + private static void AssertRemoveFromRelationshipNotBlocked(HasManyAttribute relationship, RequestAdapterState state) + { + if (!relationship.Capabilities.HasFlag(HasManyCapabilities.AllowRemove)) + { + throw new ModelConversionException(state.Position, "Relationship cannot be removed from.", + $"The relationship '{relationship.PublicName}' on resource type '{relationship.LeftType.PublicName}' cannot be removed from."); + } + } } diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs index b489665a41..1b85b35336 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceObjectAdapter.cs @@ -63,8 +63,8 @@ private void ConvertAttribute(IIdentifiable resource, string attributeName, obje AssertIsKnownAttribute(attr, attributeName, resourceType, state); AssertNoInvalidAttribute(attributeValue, state); - AssertNoBlockedCreate(attr, resourceType, state); - AssertNoBlockedChange(attr, resourceType, state); + AssertSetAttributeInCreateResourceNotBlocked(attr, resourceType, state); + AssertSetAttributeInUpdateResourceNotBlocked(attr, resourceType, state); AssertNotReadOnly(attr, resourceType, state); attr.SetValue(resource, attributeValue); @@ -96,7 +96,7 @@ private static void AssertNoInvalidAttribute(object? attributeValue, RequestAdap } } - private static void AssertNoBlockedCreate(AttrAttribute attr, ResourceType resourceType, RequestAdapterState state) + private static void AssertSetAttributeInCreateResourceNotBlocked(AttrAttribute attr, ResourceType resourceType, RequestAdapterState state) { if (state.Request.WriteOperation == WriteOperationKind.CreateResource && !attr.Capabilities.HasFlag(AttrCapabilities.AllowCreate)) { @@ -105,7 +105,7 @@ private static void AssertNoBlockedCreate(AttrAttribute attr, ResourceType resou } } - private static void AssertNoBlockedChange(AttrAttribute attr, ResourceType resourceType, RequestAdapterState state) + private static void AssertSetAttributeInUpdateResourceNotBlocked(AttrAttribute attr, ResourceType resourceType, RequestAdapterState state) { if (state.Request.WriteOperation == WriteOperationKind.UpdateResource && !attr.Capabilities.HasFlag(AttrCapabilities.AllowChange)) { @@ -148,6 +148,7 @@ private void ConvertRelationship(string relationshipName, RelationshipObject? re } AssertIsKnownRelationship(relationship, relationshipName, resourceType, state); + AssertRelationshipChangeNotBlocked(relationship, state); object? rightValue = _relationshipDataAdapter.Convert(relationshipObject.Data, relationship, true, state); diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs index 5dc97f8052..b1398b7cfb 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs @@ -287,6 +287,13 @@ private void TraverseRelationship(RelationshipAttribute relationship, IIdentifia ? leftTreeNode.ResourceType.GetRelationshipByPropertyName(relationship.Property.Name) : relationship; + if (effectiveRelationship.IsViewBlocked()) + { + // Hide related resources when blocked. According to JSON:API, breaking full linkage is only allowed + // when the client explicitly requested it by sending a sparse fieldset. + return; + } + object? rightValue = effectiveRelationship.GetValue(leftResource); IReadOnlyCollection rightResources = CollectionConverter.ExtractResources(rightValue); diff --git a/test/AnnotationTests/Models/TreeNode.cs b/test/AnnotationTests/Models/TreeNode.cs index 955db81720..9002773680 100644 --- a/test/AnnotationTests/Models/TreeNode.cs +++ b/test/AnnotationTests/Models/TreeNode.cs @@ -12,9 +12,9 @@ public sealed class TreeNode : Identifiable [Attr(PublicName = "name", Capabilities = AttrCapabilities.AllowSort)] public string? DisplayName { get; set; } - [HasOne(PublicName = "orders", CanInclude = true, Links = LinkTypes.All)] + [HasOne(PublicName = "orders", Capabilities = HasOneCapabilities.AllowView | HasOneCapabilities.AllowInclude, Links = LinkTypes.All)] public TreeNode? Parent { get; set; } - [HasMany(PublicName = "orders", CanInclude = true, Links = LinkTypes.All)] + [HasMany(PublicName = "orders", Capabilities = HasManyCapabilities.AllowView | HasManyCapabilities.AllowFilter, Links = LinkTypes.All)] public ISet Children { get; set; } = new HashSet(); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs index ffae461fb0..048ef5506f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs @@ -751,48 +751,6 @@ public async Task Cannot_create_resource_for_unknown_type() error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } - [Fact] - public async Task Cannot_create_resource_attribute_with_blocked_capability() - { - // Arrange - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "add", - data = new - { - type = "lyrics", - attributes = new - { - createdAt = 12.July(1980) - } - } - } - } - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - - responseDocument.Errors.ShouldHaveCount(1); - - ErrorObject error = responseDocument.Errors[0]; - error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); - error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when creating resource."); - error.Detail.Should().Be("The attribute 'createdAt' on resource type 'lyrics' cannot be assigned to."); - error.Source.ShouldNotBeNull(); - error.Source.Pointer.Should().Be("/atomic:operations[0]/data/attributes/createdAt"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); - } - [Fact] public async Task Cannot_create_resource_with_readonly_attribute() { @@ -990,4 +948,46 @@ await _testContext.RunOnDatabaseAsync(async dbContext => trackInDatabase.Performers[0].Id.Should().Be(existingPerformer.Id); }); } + + [Fact] + public async Task Cannot_assign_attribute_with_blocked_capability() + { + // Arrange + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "lyrics", + attributes = new + { + createdAt = 12.July(1980) + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when creating resource."); + error.Detail.Should().Be("The attribute 'createdAt' on resource type 'lyrics' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/attributes/createdAt"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs index be9700c3d6..61a1db5164 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs @@ -684,4 +684,56 @@ public async Task Cannot_create_with_object_data_in_ManyToMany_relationship() error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/tracks/data"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "musicTracks", + relationships = new + { + occursIn = new + { + data = new[] + { + new + { + type = "playlists", + id = Unknown.StringId.For() + } + } + } + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'occursIn' on resource type 'musicTracks' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/occursIn"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs index e7ba0d5288..0112dc9ed4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs @@ -734,4 +734,53 @@ await _testContext.RunOnDatabaseAsync(async dbContext => trackInDatabase.OwnedBy.Id.Should().Be(existingCompany.Id); }); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "lyrics", + relationships = new + { + language = new + { + data = new + { + type = "textLanguages", + id = Unknown.StringId.For() + } + } + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'language' on resource type 'lyrics' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/language"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs index 2baa9ac431..af1ac9e18b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs @@ -17,7 +17,7 @@ public sealed class Lyric : Identifiable [Attr(Capabilities = AttrCapabilities.None)] public DateTimeOffset CreatedAt { get; set; } - [HasOne] + [HasOne(Capabilities = HasOneCapabilities.All & ~HasOneCapabilities.AllowSet)] public TextLanguage? Language { get; set; } [HasOne] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs index 42bbbdfd3b..646a0d9ed9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs @@ -34,6 +34,6 @@ public sealed class MusicTrack : Identifiable [HasMany] public IList Performers { get; set; } = new List(); - [HasMany] + [HasMany(Capabilities = HasManyCapabilities.All & ~(HasManyCapabilities.AllowSet | HasManyCapabilities.AllowAdd | HasManyCapabilities.AllowRemove))] public IList OccursIn { get; set; } = new List(); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs index fc13524e8a..92f1bc638b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs @@ -1081,4 +1081,57 @@ await _testContext.RunOnDatabaseAsync(async dbContext => trackInDatabase.Performers[0].Id.Should().Be(existingTrack.Performers[0].Id); }); } + + [Fact] + public async Task Cannot_add_with_blocked_capability() + { + // Arrange + MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.MusicTracks.Add(existingTrack); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + @ref = new + { + type = "musicTracks", + id = existingTrack.StringId, + relationship = "occursIn" + }, + data = new + { + type = "playlists", + id = Unknown.StringId.For() + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be added to."); + error.Detail.Should().Be("The relationship 'occursIn' on resource type 'musicTracks' cannot be added to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs index d015cae3fd..bed9b62d99 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs @@ -1042,4 +1042,57 @@ await _testContext.RunOnDatabaseAsync(async dbContext => trackInDatabase.Performers[0].Id.Should().Be(existingTrack.Performers[0].Id); }); } + + [Fact] + public async Task Cannot_remove_with_blocked_capability() + { + // Arrange + MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.MusicTracks.Add(existingTrack); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "remove", + @ref = new + { + type = "musicTracks", + id = existingTrack.StringId, + relationship = "occursIn" + }, + data = new + { + type = "playlists", + id = Unknown.StringId.For() + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be removed from."); + error.Detail.Should().Be("The relationship 'occursIn' on resource type 'musicTracks' cannot be removed from."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs index faf8a9cb8c..be0da69ad9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs @@ -1140,4 +1140,57 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Source.Pointer.Should().Be("/atomic:operations[0]/data[0]/type"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.MusicTracks.Add(existingTrack); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "musicTracks", + id = existingTrack.StringId, + relationship = "occursIn" + }, + data = new + { + type = "playlists", + id = Unknown.StringId.For() + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'occursIn' on resource type 'musicTracks' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs index 4b650a3d85..31b62a7c8e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs @@ -1305,4 +1305,57 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Source.Pointer.Should().Be("/atomic:operations[0]/data/type"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + Lyric existingLyric = _fakers.Lyric.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Lyrics.Add(existingLyric); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + @ref = new + { + type = "lyrics", + id = existingLyric.StringId, + relationship = "language" + }, + data = new + { + type = "textLanguages", + id = Unknown.StringId.For() + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'language' on resource type 'lyrics' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/ref/relationship"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs index fa801c67c1..0d8c5e1d80 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs @@ -799,4 +799,65 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/performers/data[0]/type"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.MusicTracks.Add(existingTrack); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + data = new + { + type = "musicTracks", + id = existingTrack.StringId, + relationships = new + { + occursIn = new + { + data = new[] + { + new + { + type = "playlists", + id = Unknown.StringId.For() + } + } + } + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'occursIn' on resource type 'musicTracks' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/occursIn"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs index 336b7d5621..eb9f81b6e6 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs @@ -1503,57 +1503,6 @@ public async Task Cannot_update_resource_for_incompatible_ID() error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } - [Fact] - public async Task Cannot_update_resource_attribute_with_blocked_capability() - { - // Arrange - Lyric existingLyric = _fakers.Lyric.Generate(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.Lyrics.Add(existingLyric); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - atomic__operations = new[] - { - new - { - op = "update", - data = new - { - type = "lyrics", - id = existingLyric.StringId, - attributes = new - { - createdAt = 12.July(1980) - } - } - } - } - }; - - const string route = "/operations"; - - // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - - responseDocument.Errors.ShouldHaveCount(1); - - ErrorObject error = responseDocument.Errors[0]; - error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); - error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when updating resource."); - error.Detail.Should().Be("The attribute 'createdAt' on resource type 'lyrics' cannot be assigned to."); - error.Source.ShouldNotBeNull(); - error.Source.Pointer.Should().Be("/atomic:operations[0]/data/attributes/createdAt"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); - } - [Fact] public async Task Cannot_update_resource_with_readonly_attribute() { @@ -1815,4 +1764,55 @@ await _testContext.RunOnDatabaseAsync(async dbContext => trackInDatabase.Performers[0].Id.Should().Be(existingPerformer.Id); }); } + + [Fact] + public async Task Cannot_assign_attribute_with_blocked_capability() + { + // Arrange + Lyric existingLyric = _fakers.Lyric.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Lyrics.Add(existingLyric); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + data = new + { + type = "lyrics", + id = existingLyric.StringId, + attributes = new + { + createdAt = 12.July(1980) + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when updating resource."); + error.Detail.Should().Be("The attribute 'createdAt' on resource type 'lyrics' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/attributes/createdAt"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs index 3996983042..931e75e789 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs @@ -1046,4 +1046,62 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/lyric/data/type"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + Lyric existingLyric = _fakers.Lyric.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Lyrics.Add(existingLyric); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "update", + data = new + { + type = "lyrics", + id = existingLyric.StringId, + relationships = new + { + language = new + { + data = new + { + type = "textLanguages", + id = Unknown.StringId.For() + } + } + } + } + } + } + }; + + const string route = "/operations"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAtomicAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'language' on resource type 'lyrics' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/relationships/language"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs index a628cf9355..1840f5674d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs @@ -29,6 +29,8 @@ public sealed class BlogPost : Identifiable [HasMany] public ISet Comments { get; set; } = new HashSet(); +#pragma warning disable CS0618 // Type or member is obsolete [HasOne(CanInclude = false)] +#pragma warning restore CS0618 // Type or member is obsolete public Blog? Parent { get; set; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs index 46d82cb7fb..eaf091ec07 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs @@ -17,6 +17,9 @@ public sealed class Calendar : Identifiable [Attr] public int DefaultAppointmentDurationInMinutes { get; set; } - [HasMany] + [HasOne] + public Appointment? MostRecentAppointment { get; set; } + + [HasMany(Capabilities = HasManyCapabilities.All & ~(HasManyCapabilities.AllowView | HasManyCapabilities.AllowFilter))] public ISet Appointments { get; set; } = new HashSet(); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs index 1af2251c6b..104f5a4f79 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs @@ -18,6 +18,7 @@ public FilterTests(IntegrationTestContext, _testContext = testContext; testContext.UseController(); + testContext.UseController(); var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); options.EnableLegacyFilterNotation = false; @@ -89,6 +90,28 @@ public async Task Cannot_filter_on_attribute_with_blocked_capability() error.Source.Parameter.Should().Be("filter"); } + [Fact] + public async Task Cannot_filter_on_ToMany_relationship_with_blocked_capability() + { + // Arrange + const string route = "/calendars?filter=has(appointments)"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.BadRequest); + error.Title.Should().Be("Filtering on the requested relationship is not allowed."); + error.Detail.Should().Be("Filtering on relationship 'appointments' is not allowed."); + error.Source.ShouldNotBeNull(); + error.Source.Parameter.Should().Be("filter"); + } + [Fact] public async Task Can_filter_on_ID() { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index c835263fc6..a3fbfe54c7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -21,6 +21,7 @@ public IncludeTests(IntegrationTestContext testContext.UseController(); testContext.UseController(); testContext.UseController(); + testContext.UseController(); var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); options.MaximumIncludeDepth = null; @@ -872,7 +873,7 @@ public async Task Cannot_include_unknown_nested_relationship() } [Fact] - public async Task Cannot_include_relationship_with_blocked_capability() + public async Task Cannot_include_relationship_when_inclusion_blocked() { // Arrange const string route = "/blogPosts?include=parent"; @@ -894,7 +895,7 @@ public async Task Cannot_include_relationship_with_blocked_capability() } [Fact] - public async Task Cannot_include_relationship_with_nested_blocked_capability() + public async Task Cannot_include_relationship_when_nested_inclusion_blocked() { // Arrange const string route = "/blogs?include=posts.parent"; @@ -915,6 +916,85 @@ public async Task Cannot_include_relationship_with_nested_blocked_capability() error.Source.Parameter.Should().Be("include"); } + [Fact] + public async Task Hides_relationship_and_related_resources_when_viewing_blocked() + { + // Arrange + Calendar calendar = _fakers.Calendar.Generate(); + calendar.Appointments = _fakers.Appointment.Generate(2).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Calendars.Add(calendar); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/calendars/{calendar.StringId}?include=appointments"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Type.Should().Be("calendars"); + responseDocument.Data.SingleValue.Id.Should().Be(calendar.StringId); + + responseDocument.Data.SingleValue.Relationships.ShouldNotBeEmpty(); + responseDocument.Data.SingleValue.Relationships.Should().NotContainKey("appointments"); + + responseDocument.Included.Should().BeEmpty(); + } + + [Fact] + public async Task Hides_relationship_but_includes_related_resource_when_viewing_blocked_but_accessible_via_other_path() + { + // Arrange + Calendar calendar = _fakers.Calendar.Generate(); + calendar.MostRecentAppointment = _fakers.Appointment.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Calendars.Add(calendar); + await dbContext.SaveChangesAsync(); + + calendar.Appointments = new[] + { + _fakers.Appointment.Generate(), + calendar.MostRecentAppointment + }.ToHashSet(); + + await dbContext.SaveChangesAsync(); + }); + + string route = $"/calendars/{calendar.StringId}?include=appointments,mostRecentAppointment"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Type.Should().Be("calendars"); + responseDocument.Data.SingleValue.Id.Should().Be(calendar.StringId); + + responseDocument.Data.SingleValue.Relationships.ShouldContainKey("mostRecentAppointment").With(value => + { + value.ShouldNotBeNull(); + value.Data.SingleValue.ShouldNotBeNull(); + value.Data.SingleValue.Type.Should().Be("appointments"); + value.Data.SingleValue.Id.Should().Be(calendar.MostRecentAppointment.StringId); + }); + + responseDocument.Data.SingleValue.Relationships.Should().NotContainKey("appointments"); + + responseDocument.Included.ShouldHaveCount(1); + responseDocument.Included[0].Type.Should().Be("appointments"); + responseDocument.Included[0].Id.Should().Be(calendar.MostRecentAppointment.StringId); + } + [Fact] public async Task Ignores_null_parent_in_nested_include() { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SerializerIgnoreConditionTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SerializerIgnoreConditionTests.cs index 6e72d6923a..0a6204cce4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SerializerIgnoreConditionTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SerializerIgnoreConditionTests.cs @@ -34,10 +34,10 @@ public async Task Applies_configuration_for_ignore_condition(JsonIgnoreCondition calendar.TimeZone = null; calendar.DefaultAppointmentDurationInMinutes = default; calendar.ShowWeekNumbers = true; - calendar.Appointments = _fakers.Appointment.Generate(1).ToHashSet(); - calendar.Appointments.Single().Description = null; - calendar.Appointments.Single().StartTime = default; - calendar.Appointments.Single().EndTime = 1.January(2001).AsUtc(); + calendar.MostRecentAppointment = _fakers.Appointment.Generate(); + calendar.MostRecentAppointment.Description = null; + calendar.MostRecentAppointment.StartTime = default; + calendar.MostRecentAppointment.EndTime = 1.January(2001).AsUtc(); await RunOnDatabaseAsync(async dbContext => { @@ -45,7 +45,7 @@ await RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - string route = $"/calendars/{calendar.StringId}?include=appointments"; + string route = $"/calendars/{calendar.StringId}?include=mostRecentAppointment"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index bbcf2cd17a..4036c62f13 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -20,6 +20,7 @@ public SparseFieldSetTests(IntegrationTestContext(); testContext.UseController(); testContext.UseController(); + testContext.UseController(); testContext.ConfigureServicesAfterStartup(services => { @@ -741,6 +742,54 @@ public async Task Cannot_select_attribute_with_blocked_capability() error.Source.Parameter.Should().Be("fields[webAccounts]"); } + [Fact] + public async Task Cannot_select_ToOne_relationship_with_blocked_capability() + { + // Arrange + WebAccount account = _fakers.WebAccount.Generate(); + + string route = $"/webAccounts/{account.Id}?fields[webAccounts]=person"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.BadRequest); + error.Title.Should().Be("Retrieving the requested relationship is not allowed."); + error.Detail.Should().Be("Retrieving the relationship 'person' is not allowed."); + error.Source.ShouldNotBeNull(); + error.Source.Parameter.Should().Be("fields[webAccounts]"); + } + + [Fact] + public async Task Cannot_select_ToMany_relationship_with_blocked_capability() + { + // Arrange + Calendar calendar = _fakers.Calendar.Generate(); + + string route = $"/calendars/{calendar.Id}?fields[calendars]=appointments"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.BadRequest); + error.Title.Should().Be("Retrieving the requested relationship is not allowed."); + error.Detail.Should().Be("Retrieving the relationship 'appointments' is not allowed."); + error.Source.ShouldNotBeNull(); + error.Source.Parameter.Should().Be("fields[calendars]"); + } + [Fact] public async Task Retrieves_all_properties_when_fieldset_contains_readonly_attribute() { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs index 1749184fa4..70133fba54 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs @@ -11,7 +11,7 @@ public sealed class WebAccount : Identifiable [Attr] public string UserName { get; set; } = null!; - [Attr(Capabilities = ~AttrCapabilities.AllowView)] + [Attr(Capabilities = AttrCapabilities.All & ~AttrCapabilities.AllowView)] public string Password { get; set; } = null!; [Attr] @@ -23,7 +23,7 @@ public sealed class WebAccount : Identifiable [Attr] public string EmailAddress { get; set; } = null!; - [HasOne] + [HasOne(Capabilities = HasOneCapabilities.All & ~HasOneCapabilities.AllowView)] public Human? Person { get; set; } [HasMany] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index f85eb6579e..587b7d8277 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -728,41 +728,6 @@ public async Task Cannot_create_on_resource_type_mismatch_between_url_and_body() error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } - [Fact] - public async Task Cannot_create_resource_attribute_with_blocked_capability() - { - // Arrange - var requestBody = new - { - data = new - { - type = "workItems", - attributes = new - { - isImportant = true - } - } - }; - - const string route = "/workItems"; - - // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - - responseDocument.Errors.ShouldHaveCount(1); - - ErrorObject error = responseDocument.Errors[0]; - error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); - error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when creating resource."); - error.Detail.Should().Be("The attribute 'isImportant' on resource type 'workItems' cannot be assigned to."); - error.Source.ShouldNotBeNull(); - error.Source.Pointer.Should().Be("/data/attributes/isImportant"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); - } - [Fact] public async Task Cannot_create_resource_with_readonly_attribute() { @@ -958,4 +923,39 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.Tags.Single().Id.Should().Be(existingTag.Id); }); } + + [Fact] + public async Task Cannot_assign_attribute_with_blocked_capability() + { + // Arrange + var requestBody = new + { + data = new + { + type = "workItems", + attributes = new + { + isImportant = true + } + } + }; + + const string route = "/workItems"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when creating resource."); + error.Detail.Should().Be("The attribute 'isImportant' on resource type 'workItems' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/attributes/isImportant"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index fb96a8b0f5..0a6ed42c60 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -17,6 +17,7 @@ public CreateResourceWithToManyRelationshipTests(IntegrationTestContext(); + testContext.UseController(); testContext.UseController(); } @@ -790,4 +791,49 @@ public async Task Cannot_create_resource_with_local_ID() error.Source.Pointer.Should().Be("/data/lid"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + var requestBody = new + { + data = new + { + type = "workItemGroups", + relationships = new + { + items = new + { + data = new[] + { + new + { + type = "workItems", + id = Unknown.StringId.For() + } + } + } + } + } + }; + + const string route = "/workItemGroups"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'items' on resource type 'workItemGroups' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/relationships/items"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index 07b97c252d..08bbcf1f63 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -762,4 +762,46 @@ public async Task Cannot_create_resource_with_local_ID() error.Source.Pointer.Should().Be("/data/lid"); error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + var requestBody = new + { + data = new + { + type = "workItems", + relationships = new + { + group = new + { + data = new + { + type = "workItemGroups", + id = Unknown.StringId.For() + } + } + } + } + }; + + const string route = "/workItems"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'group' on resource type 'workItems' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/relationships/group"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index 5a4a3b226b..08ac8c3f54 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -17,6 +17,7 @@ public AddToToManyRelationshipTests(IntegrationTestContext(); + testContext.UseController(); } [Fact] @@ -933,4 +934,46 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.RelatedTo.Should().ContainSingle(workItem => workItem.Id == existingWorkItem.RelatedTo[0].Id); }); } + + [Fact] + public async Task Cannot_add_with_blocked_capability() + { + // Arrange + WorkItemGroup existingWorkItemGroup = _fakers.WorkItemGroup.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Groups.Add(existingWorkItemGroup); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "workItems", + id = Unknown.StringId.For() + } + } + }; + + string route = $"/workItemGroups/{existingWorkItemGroup.StringId}/relationships/items"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be added to."); + error.Detail.Should().Be("The relationship 'items' on resource type 'workItemGroups' cannot be added to."); + error.Source.Should().BeNull(); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index 684575b860..eef972dd1f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -23,6 +23,7 @@ public RemoveFromToManyRelationshipTests(IntegrationTestContext(); + testContext.UseController(); testContext.ConfigureServicesAfterStartup(services => { @@ -1055,6 +1056,48 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }); } + [Fact] + public async Task Cannot_remove_with_blocked_capability() + { + // Arrange + WorkItemGroup existingWorkItemGroup = _fakers.WorkItemGroup.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Groups.Add(existingWorkItemGroup); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "workItems", + id = Unknown.StringId.For() + } + } + }; + + string route = $"/workItemGroups/{existingWorkItemGroup.StringId}/relationships/items"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteDeleteAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be removed from."); + error.Detail.Should().Be("The relationship 'items' on resource type 'workItemGroups' cannot be removed from."); + error.Source.Should().BeNull(); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] private sealed class RemoveExtraFromWorkItemDefinition : JsonApiResourceDefinition { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index fb368fd524..02a7e68292 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -18,6 +18,7 @@ public ReplaceToManyRelationshipTests(IntegrationTestContext(); + testContext.UseController(); } [Fact] @@ -1013,4 +1014,46 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.RelatedTo[0].Id.Should().Be(existingWorkItem.Id); }); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + WorkItemGroup existingWorkItemGroup = _fakers.WorkItemGroup.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Groups.Add(existingWorkItemGroup); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new[] + { + new + { + type = "workItems", + id = Unknown.StringId.For() + } + } + }; + + string route = $"/workItemGroups/{existingWorkItemGroup.StringId}/relationships/items"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'items' on resource type 'workItemGroups' cannot be assigned to."); + error.Source.Should().BeNull(); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index 1ba844be78..a492cc4826 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -773,4 +773,43 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.Parent.Id.Should().Be(existingWorkItem.Id); }); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + WorkItem existingWorkItem = _fakers.WorkItem.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.WorkItems.Add(existingWorkItem); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "workItemGroups", + id = Unknown.StringId.For() + } + }; + + string route = $"/workItems/{existingWorkItem.StringId}/relationships/group"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'group' on resource type 'workItems' cannot be assigned to."); + error.Source.Should().BeNull(); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index 0ab3ab93b2..be673f660e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -19,6 +19,7 @@ public ReplaceToManyRelationshipTests(IntegrationTestContext(); + testContext.UseController(); testContext.UseController(); testContext.ConfigureServicesAfterStartup(services => @@ -1134,4 +1135,58 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.RelatedTo[0].Id.Should().Be(existingWorkItem.Id); }); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + WorkItemGroup existingWorkItemGroup = _fakers.WorkItemGroup.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Groups.Add(existingWorkItemGroup); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "workItemGroups", + id = existingWorkItemGroup.StringId, + relationships = new + { + items = new + { + data = new[] + { + new + { + type = "workItems", + id = Unknown.StringId.For() + } + } + } + } + } + }; + + string route = $"/workItemGroups/{existingWorkItemGroup.StringId}"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'items' on resource type 'workItemGroups' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/relationships/items"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index b67090e22f..dc5fb35996 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -1085,50 +1085,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } - [Fact] - public async Task Cannot_update_resource_attribute_with_blocked_capability() - { - // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - - await _testContext.RunOnDatabaseAsync(async dbContext => - { - dbContext.WorkItems.Add(existingWorkItem); - await dbContext.SaveChangesAsync(); - }); - - var requestBody = new - { - data = new - { - type = "workItems", - id = existingWorkItem.StringId, - attributes = new - { - isImportant = true - } - } - }; - - string route = $"/workItems/{existingWorkItem.StringId}"; - - // Act - (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); - - // Assert - httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - - responseDocument.Errors.ShouldHaveCount(1); - - ErrorObject error = responseDocument.Errors[0]; - error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); - error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when updating resource."); - error.Detail.Should().Be("The attribute 'isImportant' on resource type 'workItems' cannot be assigned to."); - error.Source.ShouldNotBeNull(); - error.Source.Pointer.Should().Be("/data/attributes/isImportant"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); - } - [Fact] public async Task Cannot_update_resource_with_readonly_attribute() { @@ -1542,4 +1498,48 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.RelatedTo.Single().Id.Should().Be(existingWorkItem.Id); }); } + + [Fact] + public async Task Cannot_assign_attribute_with_blocked_capability() + { + // Arrange + WorkItem existingWorkItem = _fakers.WorkItem.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.WorkItems.Add(existingWorkItem); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "workItems", + id = existingWorkItem.StringId, + attributes = new + { + isImportant = true + } + } + }; + + string route = $"/workItems/{existingWorkItem.StringId}"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Attribute value cannot be assigned when updating resource."); + error.Detail.Should().Be("The attribute 'isImportant' on resource type 'workItems' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/attributes/isImportant"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index be3c9b89b2..81faaa8297 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -941,4 +941,55 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.Parent.Id.Should().Be(existingWorkItem.Id); }); } + + [Fact] + public async Task Cannot_assign_relationship_with_blocked_capability() + { + // Arrange + WorkItem existingWorkItem = _fakers.WorkItem.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.WorkItems.Add(existingWorkItem); + await dbContext.SaveChangesAsync(); + }); + + var requestBody = new + { + data = new + { + type = "workItems", + id = existingWorkItem.StringId, + relationships = new + { + group = new + { + data = new + { + type = "workItemGroups", + id = Unknown.StringId.For() + } + } + } + } + }; + + string route = $"/workItems/{existingWorkItem.StringId}"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.ShouldHaveCount(1); + + ErrorObject error = responseDocument.Errors[0]; + error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + error.Title.Should().Be("Failed to deserialize request body: Relationship cannot be assigned."); + error.Detail.Should().Be("The relationship 'group' on resource type 'workItems' cannot be assigned to."); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/data/relationships/group"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs index e7a0a01b5c..c722592e83 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs @@ -19,7 +19,7 @@ public sealed class WorkItem : Identifiable public WorkItemPriority Priority { get; set; } [NotMapped] - [Attr(Capabilities = ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] + [Attr(Capabilities = AttrCapabilities.All & ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] public bool IsImportant { get => Priority == WorkItemPriority.High; @@ -47,6 +47,6 @@ public bool IsImportant [HasMany] public IList RelatedTo { get; set; } = new List(); - [HasOne] + [HasOne(Capabilities = HasOneCapabilities.All & ~HasOneCapabilities.AllowSet)] public WorkItemGroup? Group { get; set; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs index 86c11391ed..7f132c8994 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs @@ -22,6 +22,6 @@ public sealed class WorkItemGroup : Identifiable [HasOne] public RgbColor? Color { get; set; } - [HasMany] + [HasMany(Capabilities = HasManyCapabilities.All & ~(HasManyCapabilities.AllowSet | HasManyCapabilities.AllowAdd | HasManyCapabilities.AllowRemove))] public IList Items { get; set; } = new List(); } From 45b1136ce35c9f8a1a0b2508d3f8e4a5a96da30c Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 1 Oct 2022 16:47:52 +0200 Subject: [PATCH 16/47] Allow empty include query string parameter value --- .../Queries/Internal/Parsing/IncludeParser.cs | 14 ++++++++--- .../IncludeQueryStringParameterReader.cs | 2 +- .../QueryStrings/Includes/IncludeTests.cs | 25 +++++++++++++++++++ .../QueryStrings/QueryStringTests.cs | 1 - .../IncludeParseTests.cs | 2 +- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs index 38ee1ffa83..3c8be88e46 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs @@ -30,12 +30,18 @@ public IncludeExpression Parse(string source, ResourceType resourceTypeInScope, protected IncludeExpression ParseInclude(ResourceType resourceTypeInScope, int? maximumDepth) { var treeRoot = IncludeTreeNode.CreateRoot(resourceTypeInScope); - - ParseRelationshipChain(treeRoot); + bool isAtStart = true; while (TokenStack.Any()) { - EatSingleCharacterToken(TokenKind.Comma); + if (!isAtStart) + { + EatSingleCharacterToken(TokenKind.Comma); + } + else + { + isAtStart = false; + } ParseRelationshipChain(treeRoot); } @@ -244,7 +250,7 @@ public IncludeExpression ToExpression() if (element.Relationship is HiddenRootRelationshipAttribute) { - return new IncludeExpression(element.Children); + return element.Children.Any() ? new IncludeExpression(element.Children) : IncludeExpression.Empty; } return new IncludeExpression(ImmutableHashSet.Create(element)); diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs index 299e8b22b2..a4db6ebd4a 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs @@ -18,7 +18,7 @@ public class IncludeQueryStringParameterReader : QueryStringParameterReader, IIn private IncludeExpression? _includeExpression; - public bool AllowEmptyValue => false; + public bool AllowEmptyValue => true; public IncludeQueryStringParameterReader(IJsonApiRequest request, IResourceGraph resourceGraph, IJsonApiOptions options) : base(request, resourceGraph) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index a3fbfe54c7..6dece6f0d1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -828,6 +828,31 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[0].Attributes.ShouldContainKey("userName").With(value => value.Should().Be(account.UserName)); } + [Fact] + public async Task Can_select_empty_includes() + { + // Arrange + WebAccount account = _fakers.WebAccount.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Accounts.Add(account); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/webAccounts/{account.StringId}?include="; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.SingleValue.ShouldNotBeNull(); + + responseDocument.Included.Should().BeEmpty(); + } + [Fact] public async Task Cannot_include_unknown_relationship() { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringTests.cs index 70a40d1e4f..0aa955a219 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringTests.cs @@ -64,7 +64,6 @@ public async Task Can_use_unknown_query_string_parameter() } [Theory] - [InlineData("include")] [InlineData("filter")] [InlineData("sort")] [InlineData("page[size]")] diff --git a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/IncludeParseTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/IncludeParseTests.cs index 7c7c28f4e3..b576b58b71 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/IncludeParseTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/QueryStringParameters/IncludeParseTests.cs @@ -50,7 +50,6 @@ public void Reader_Is_Enabled(JsonApiQueryStringParameters parametersDisabled, b } [Theory] - [InlineData("includes", "", "Relationship name expected.")] [InlineData("includes", " ", "Unexpected whitespace.")] [InlineData("includes", ",", "Relationship name expected.")] [InlineData("includes", "posts,", "Relationship name expected.")] @@ -85,6 +84,7 @@ public void Reader_Read_Fails(string parameterName, string parameterValue, strin } [Theory] + [InlineData("includes", "", "")] [InlineData("includes", "owner", "owner")] [InlineData("includes", "posts", "posts")] [InlineData("includes", "owner.posts", "owner.posts")] From f2c6029989f3e59616f5edce8f54a291a653ab1e Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 1 Oct 2022 18:30:21 +0200 Subject: [PATCH 17/47] Fixed: full linkage does not allow primary resources to occur in included --- .../Response/ResourceObjectTreeNode.cs | 12 +++++- .../QueryStrings/Includes/IncludeTests.cs | 22 ++++------ .../Reading/IClientSettingsProvider.cs | 2 +- .../ResourceDefinitions/Reading/Moon.cs | 3 ++ .../Reading/MoonDefinition.cs | 8 ++-- .../Reading/ResourceDefinitionReadTests.cs | 42 ++++++++++--------- .../Reading/TestClientSettingsProvider.cs | 8 ++-- 7 files changed, 54 insertions(+), 43 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs index 01743168be..9527f766e1 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs @@ -181,7 +181,17 @@ public IList GetResponseIncluded() VisitRelationshipChildrenInSubtree(child, visited); } - return visited.Select(node => node.ResourceObject).ToArray(); + List includes = visited.Select(node => node.ResourceObject).ToList(); + + foreach (ResourceObject primaryResourceObjects in GetDirectChildren().Select(node => node.ResourceObject)) + { + if (includes.Contains(primaryResourceObjects, ResourceObjectComparer.Instance)) + { + includes.Remove(primaryResourceObjects); + } + } + + return includes; } private IList GetDirectChildren() diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index 6dece6f0d1..88360bcfa2 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -398,29 +398,25 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.SingleValue.Id.Should().Be(comment.StringId); responseDocument.Data.SingleValue.Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Text)); - responseDocument.Included.ShouldHaveCount(5); + responseDocument.Included.ShouldHaveCount(4); responseDocument.Included[0].Type.Should().Be("blogPosts"); responseDocument.Included[0].Id.Should().Be(comment.Parent.StringId); responseDocument.Included[0].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(comment.Parent.Caption)); responseDocument.Included[1].Type.Should().Be("comments"); - responseDocument.Included[1].Id.Should().Be(comment.StringId); - responseDocument.Included[1].Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Text)); - - responseDocument.Included[2].Type.Should().Be("comments"); - responseDocument.Included[2].Id.Should().Be(comment.Parent.Comments.ElementAt(0).StringId); - responseDocument.Included[2].Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Parent.Comments.ElementAt(0).Text)); + responseDocument.Included[1].Id.Should().Be(comment.Parent.Comments.ElementAt(0).StringId); + responseDocument.Included[1].Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Parent.Comments.ElementAt(0).Text)); string userName = comment.Parent.Comments.ElementAt(0).Author!.UserName; - responseDocument.Included[3].Type.Should().Be("webAccounts"); - responseDocument.Included[3].Id.Should().Be(comment.Parent.Comments.ElementAt(0).Author!.StringId); - responseDocument.Included[3].Attributes.ShouldContainKey("userName").With(value => value.Should().Be(userName)); + responseDocument.Included[2].Type.Should().Be("webAccounts"); + responseDocument.Included[2].Id.Should().Be(comment.Parent.Comments.ElementAt(0).Author!.StringId); + responseDocument.Included[2].Attributes.ShouldContainKey("userName").With(value => value.Should().Be(userName)); - responseDocument.Included[4].Type.Should().Be("comments"); - responseDocument.Included[4].Id.Should().Be(comment.Parent.Comments.ElementAt(1).StringId); - responseDocument.Included[4].Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Parent.Comments.ElementAt(1).Text)); + responseDocument.Included[3].Type.Should().Be("comments"); + responseDocument.Included[3].Id.Should().Be(comment.Parent.Comments.ElementAt(1).StringId); + responseDocument.Included[3].Attributes.ShouldContainKey("text").With(value => value.Should().Be(comment.Parent.Comments.ElementAt(1).Text)); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/IClientSettingsProvider.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/IClientSettingsProvider.cs index 65f32a4f65..f67cd3d993 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/IClientSettingsProvider.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/IClientSettingsProvider.cs @@ -4,5 +4,5 @@ public interface IClientSettingsProvider { bool IsIncludePlanetMoonsBlocked { get; } bool ArePlanetsWithPrivateNameHidden { get; } - bool IsMoonOrbitingPlanetAutoIncluded { get; } + bool IsStarGivingLightToMoonAutoIncluded { get; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs index 0a394c6d17..f6d16fed1b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs @@ -16,4 +16,7 @@ public sealed class Moon : Identifiable [HasOne] public Planet OrbitsAround { get; set; } = null!; + + [HasOne] + public Star? IsGivenLightBy { get; set; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index 7ef545b462..bdd75a9aff 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -28,15 +28,15 @@ public override IImmutableSet OnApplyIncludes(IImmutab { base.OnApplyIncludes(existingIncludes); - if (!_clientSettingsProvider.IsMoonOrbitingPlanetAutoIncluded || - existingIncludes.Any(include => include.Relationship.Property.Name == nameof(Moon.OrbitsAround))) + if (!_clientSettingsProvider.IsStarGivingLightToMoonAutoIncluded || + existingIncludes.Any(include => include.Relationship.Property.Name == nameof(Moon.IsGivenLightBy))) { return existingIncludes; } - RelationshipAttribute orbitsAroundRelationship = ResourceType.GetRelationshipByPropertyName(nameof(Moon.OrbitsAround)); + RelationshipAttribute isGivenLightByRelationship = ResourceType.GetRelationshipByPropertyName(nameof(Moon.IsGivenLightBy)); - return existingIncludes.Add(new IncludeElementExpression(orbitsAroundRelationship)); + return existingIncludes.Add(new IncludeElementExpression(isGivenLightByRelationship)); } public override QueryStringParameterHandlers OnRegisterQueryableHandlersForQueryStringParameters() diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs index 83dececbec..db80d4c14b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs @@ -93,10 +93,11 @@ public async Task Include_from_resource_definition_is_added() var hitCounter = _testContext.Factory.Services.GetRequiredService(); var settingsProvider = (TestClientSettingsProvider)_testContext.Factory.Services.GetRequiredService(); - settingsProvider.AutoIncludeOrbitingPlanetForMoons(); + settingsProvider.AutoIncludeStarGivingLightToMoon(); Moon moon = _fakers.Moon.Generate(); moon.OrbitsAround = _fakers.Planet.Generate(); + moon.IsGivenLightBy = _fakers.Star.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -114,18 +115,18 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.SingleValue.ShouldNotBeNull(); - responseDocument.Data.SingleValue.Relationships.ShouldContainKey("orbitsAround").With(value => + responseDocument.Data.SingleValue.Relationships.ShouldContainKey("isGivenLightBy").With(value => { value.ShouldNotBeNull(); value.Data.SingleValue.ShouldNotBeNull(); - value.Data.SingleValue.Type.Should().Be("planets"); - value.Data.SingleValue.Id.Should().Be(moon.OrbitsAround.StringId); + value.Data.SingleValue.Type.Should().Be("stars"); + value.Data.SingleValue.Id.Should().Be(moon.IsGivenLightBy.StringId); }); responseDocument.Included.ShouldHaveCount(1); - responseDocument.Included[0].Type.Should().Be("planets"); - responseDocument.Included[0].Id.Should().Be(moon.OrbitsAround.StringId); - responseDocument.Included[0].Attributes.ShouldContainKey("publicName").With(value => value.Should().Be(moon.OrbitsAround.PublicName)); + responseDocument.Included[0].Type.Should().Be("stars"); + responseDocument.Included[0].Id.Should().Be(moon.IsGivenLightBy.StringId); + responseDocument.Included[0].Attributes.ShouldContainKey("name").With(value => value.Should().Be(moon.IsGivenLightBy.Name)); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { @@ -134,12 +135,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplySort), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), - (typeof(Planet), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), - (typeof(Planet), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), (typeof(Moon), ResourceDefinitionExtensibilityPoints.GetMeta), - (typeof(Planet), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), - (typeof(Planet), ResourceDefinitionExtensibilityPoints.GetMeta) + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), + (typeof(Star), ResourceDefinitionExtensibilityPoints.GetMeta) }, options => options.WithStrictOrdering()); } @@ -150,11 +151,11 @@ public async Task Include_from_included_resource_definition_is_added() var hitCounter = _testContext.Factory.Services.GetRequiredService(); var settingsProvider = (TestClientSettingsProvider)_testContext.Factory.Services.GetRequiredService(); - settingsProvider.AutoIncludeOrbitingPlanetForMoons(); + settingsProvider.AutoIncludeStarGivingLightToMoon(); Planet planet = _fakers.Planet.Generate(); planet.Moons = _fakers.Moon.Generate(1).ToHashSet(); - planet.Moons.ElementAt(0).OrbitsAround = _fakers.Planet.Generate(); + planet.Moons.ElementAt(0).IsGivenLightBy = _fakers.Star.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -178,11 +179,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[0].Id.Should().Be(planet.Moons.ElementAt(0).StringId); responseDocument.Included[0].Attributes.ShouldContainKey("name").With(value => value.Should().Be(planet.Moons.ElementAt(0).Name)); - string moonName = planet.Moons.ElementAt(0).OrbitsAround.PublicName; - - responseDocument.Included[1].Type.Should().Be("planets"); - responseDocument.Included[1].Id.Should().Be(planet.Moons.ElementAt(0).OrbitsAround.StringId); - responseDocument.Included[1].Attributes.ShouldContainKey("publicName").With(value => value.Should().Be(moonName)); + responseDocument.Included[1].Type.Should().Be("stars"); + responseDocument.Included[1].Id.Should().Be(planet.Moons.ElementAt(0).IsGivenLightBy!.StringId); + responseDocument.Included[1].Attributes.ShouldContainKey("name").With(value => value.Should().Be(planet.Moons.ElementAt(0).IsGivenLightBy!.Name)); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { @@ -196,11 +195,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplyPagination), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), - (typeof(Planet), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplyIncludes), (typeof(Planet), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), (typeof(Planet), ResourceDefinitionExtensibilityPoints.GetMeta), (typeof(Moon), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), - (typeof(Moon), ResourceDefinitionExtensibilityPoints.GetMeta) + (typeof(Moon), ResourceDefinitionExtensibilityPoints.GetMeta), + (typeof(Star), ResourceDefinitionExtensibilityPoints.OnApplySparseFieldSet), + (typeof(Star), ResourceDefinitionExtensibilityPoints.GetMeta) }, options => options.WithStrictOrdering()); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/TestClientSettingsProvider.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/TestClientSettingsProvider.cs index 63f0033119..0efc7a415e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/TestClientSettingsProvider.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/TestClientSettingsProvider.cs @@ -4,13 +4,13 @@ internal sealed class TestClientSettingsProvider : IClientSettingsProvider { public bool IsIncludePlanetMoonsBlocked { get; private set; } public bool ArePlanetsWithPrivateNameHidden { get; private set; } - public bool IsMoonOrbitingPlanetAutoIncluded { get; private set; } + public bool IsStarGivingLightToMoonAutoIncluded { get; private set; } public void ResetToDefaults() { IsIncludePlanetMoonsBlocked = false; ArePlanetsWithPrivateNameHidden = false; - IsMoonOrbitingPlanetAutoIncluded = false; + IsStarGivingLightToMoonAutoIncluded = false; } public void BlockIncludePlanetMoons() @@ -23,8 +23,8 @@ public void HidePlanetsWithPrivateName() ArePlanetsWithPrivateNameHidden = true; } - public void AutoIncludeOrbitingPlanetForMoons() + public void AutoIncludeStarGivingLightToMoon() { - IsMoonOrbitingPlanetAutoIncluded = true; + IsStarGivingLightToMoonAutoIncluded = true; } } From 935224d963a8a2dde1292890a4ab51fcde3d33c9 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 3 Oct 2022 22:31:01 +0200 Subject: [PATCH 18/47] Remove unneeded dependencies --- src/JsonApiDotNetCore/JsonApiDotNetCore.csproj | 1 - test/TestBuildingBlocks/TestBuildingBlocks.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index e401db38fa..9b73a09253 100644 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -43,6 +43,5 @@ - diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index ed335f630f..5600104fda 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -13,7 +13,6 @@ - From 87265184d4a91d438ef94630523b257b0273c497 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 3 Oct 2022 22:37:22 +0200 Subject: [PATCH 19/47] Fix SourceLink and IntelliSense on doc-comments in Annotations --- .../JsonApiDotNetCore.Annotations.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj index 5f4d7c1d76..1fe6858b96 100644 --- a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj +++ b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj @@ -1,4 +1,4 @@ - + $(TargetFrameworkName);netstandard1.0 true @@ -45,4 +45,9 @@ + + + + + From b13ede9a8ce9c9985b451fa268c3a9dd1628f725 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 7 Oct 2022 04:47:52 +0200 Subject: [PATCH 20/47] Update compatibility table --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35adb3fe9f..e9b17a7179 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,10 @@ See also our [versioning policy](./VERSIONING_POLICY.md). | | | Core 3.1 | 5 | | | | 5 | 5 | | | | 6 | 5 | -| v5.x | Stable | 6 | 6 | +| v5.0.0-5.0.2 | Stable | 6 | 6 | +| v5.0.3+ | Stable | 6 | 6 | +| | | 6 | 7 | +| | | 7 | 7 | ## Contributing From cdf3c0bc8194953477be3116876be333589278e3 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 7 Oct 2022 04:49:29 +0200 Subject: [PATCH 21/47] Update compatibility table (remove v) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9b17a7179..26432ee909 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,8 @@ See also our [versioning policy](./VERSIONING_POLICY.md). | | | Core 3.1 | 5 | | | | 5 | 5 | | | | 6 | 5 | -| v5.0.0-5.0.2 | Stable | 6 | 6 | -| v5.0.3+ | Stable | 6 | 6 | +| 5.0.0-5.0.2 | Stable | 6 | 6 | +| 5.0.3+ | Stable | 6 | 6 | | | | 6 | 7 | | | | 7 | 7 | From 2113302dd3028768de9821c49be00f982aac4e65 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 19 Oct 2022 17:30:01 +0200 Subject: [PATCH 22/47] Update docs/usage/resources/relationships.md Co-authored-by: Maurits Moeys --- docs/usage/resources/relationships.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/resources/relationships.md b/docs/usage/resources/relationships.md index 14ff2eb7f5..d1f36a89a9 100644 --- a/docs/usage/resources/relationships.md +++ b/docs/usage/resources/relationships.md @@ -180,7 +180,7 @@ This can be overridden per relationship. Indicates whether the relationship can be returned in responses. When not allowed and requested using `?fields[]=`, it results in an HTTP 400 response. Otherwise, the relationship (and its related resources, when included) are silently omitted. -Note this setting does not affect retrieving the related resources directly. +Note that this setting does not affect retrieving the related resources directly. ```c# #nullable enable From 4b725bd26359af123f4fb1280cde3bb8c67ac1bc Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 19 Oct 2022 19:08:12 +0200 Subject: [PATCH 23/47] Fixed typo --- .../Serialization/Response/ResourceObjectTreeNode.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs index 9527f766e1..4c3a44fe38 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs @@ -183,11 +183,11 @@ public IList GetResponseIncluded() List includes = visited.Select(node => node.ResourceObject).ToList(); - foreach (ResourceObject primaryResourceObjects in GetDirectChildren().Select(node => node.ResourceObject)) + foreach (ResourceObject primaryResourceObject in GetDirectChildren().Select(node => node.ResourceObject)) { - if (includes.Contains(primaryResourceObjects, ResourceObjectComparer.Instance)) + if (includes.Contains(primaryResourceObject, ResourceObjectComparer.Instance)) { - includes.Remove(primaryResourceObjects); + includes.Remove(primaryResourceObject); } } From 01e132bdbb851fc88d21acdb191c6d83e9868fe0 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 20 Oct 2022 03:29:28 +0200 Subject: [PATCH 24/47] Correct pattern that is used in null-check refactorings --- JsonApiDotNetCore.sln.DotSettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index 1ffdf4a909..05a03c584b 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -3,7 +3,7 @@ // $EXPR$ -- source expression // $NAME$ -- source name (string literal or 'nameof' expression) // $MESSAGE$ -- string literal in the form of "$NAME$ != null" -JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$); +JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$); 199 5000 99 From 993ed61788f1ad819481364fe5b572900afe935b Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 26 Oct 2022 01:59:40 +0200 Subject: [PATCH 25/47] Updates tests to write SQL statements to the Output Window, so we don't need to edit appsettings.json when debugging --- .../Archiving/TelevisionDbContext.cs | 3 ++- .../AtomicOperations/OperationsDbContext.cs | 3 ++- .../Transactions/ExtraDbContext.cs | 3 ++- .../IntegrationTests/Blobs/BlobDbContext.cs | 3 ++- .../CompositeKeys/CompositeDbContext.cs | 3 ++- .../ContentNegotiation/PolicyDbContext.cs | 3 ++- .../ActionResultDbContext.cs | 3 ++- .../CustomRoutes/CustomRouteDbContext.cs | 3 ++- .../EagerLoading/EagerLoadingDbContext.cs | 3 ++- .../ExceptionHandling/ErrorDbContext.cs | 3 ++- .../HostingInIIS/HostingDbContext.cs | 3 ++- .../HostingInIIS/HostingStartup.cs | 3 +-- .../IdObfuscation/ObfuscationDbContext.cs | 3 ++- .../ModelState/ModelStateDbContext.cs | 3 ++- .../RequestBody/WorkflowDbContext.cs | 3 ++- .../IntegrationTests/Links/LinksDbContext.cs | 3 ++- .../Logging/LoggingDbContext.cs | 3 ++- .../IntegrationTests/Meta/MetaDbContext.cs | 3 ++- .../FireForgetDbContext.cs | 3 ++- .../OutboxDbContext.cs | 3 ++- .../MultiTenancy/MultiTenancyDbContext.cs | 3 ++- .../KebabCasingConventionStartup.cs | 3 +-- .../NamingConventions/NamingDbContext.cs | 3 ++- .../PascalCasingConventionStartup.cs | 3 +-- .../NonJsonApiControllers/EmptyDbContext.cs | 3 ++- .../NonJsonApiControllers/KnownDbContext.cs | 3 ++- .../QueryStrings/Filtering/FilterDbContext.cs | 3 ++- .../QueryStrings/QueryStringDbContext.cs | 3 ++- .../ReadWrite/ReadWriteDbContext.cs | 3 ++- .../DefaultBehaviorDbContext.cs | 3 ++- .../InjectionDbContext.cs | 3 ++- .../Reading/UniverseDbContext.cs | 3 ++- .../Serialization/SerializationDbContext.cs | 3 ++- .../ResourceInheritanceDbContext.cs | 3 ++- .../RestrictionDbContext.cs | 3 ++- .../Serialization/SerializationDbContext.cs | 3 ++- .../SoftDeletion/SoftDeletionDbContext.cs | 3 ++- .../ZeroKeys/ZeroKeyDbContext.cs | 3 ++- .../AbsoluteLinksInApiNamespaceStartup.cs | 3 +-- .../AbsoluteLinksNoNamespaceStartup.cs | 3 +-- .../Startups/NoModelStateValidationStartup.cs | 3 +-- .../RelativeLinksInApiNamespaceStartup.cs | 3 +-- .../RelativeLinksNoNamespaceStartup.cs | 3 +-- .../DependencyContainerRegistrationTests.cs | 2 +- .../IntegrationTestContext.cs | 2 +- test/TestBuildingBlocks/TestableDbContext.cs | 20 +++++++++++++++++++ test/TestBuildingBlocks/TestableStartup.cs | 3 +-- .../ServiceCollectionExtensionsTests.cs | 3 ++- 48 files changed, 103 insertions(+), 56 deletions(-) create mode 100644 test/TestBuildingBlocks/TestableDbContext.cs diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionDbContext.cs index 471f471028..f378dd5846 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class TelevisionDbContext : DbContext +public sealed class TelevisionDbContext : TestableDbContext { public DbSet Networks => Set(); public DbSet Stations => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs index 45be92ce8b..26dd815521 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class OperationsDbContext : DbContext +public sealed class OperationsDbContext : TestableDbContext { public DbSet Playlists => Set(); public DbSet MusicTracks => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/ExtraDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/ExtraDbContext.cs index 85fe191e86..8638855a46 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/ExtraDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/ExtraDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.Transactions; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ExtraDbContext : DbContext +public sealed class ExtraDbContext : TestableDbContext { public ExtraDbContext(DbContextOptions options) : base(options) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Blobs/BlobDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Blobs/BlobDbContext.cs index cadbb08b56..88a891c319 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Blobs/BlobDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Blobs/BlobDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Blobs; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class BlobDbContext : DbContext +public sealed class BlobDbContext : TestableDbContext { public DbSet ImageContainers => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs index e9e12439e6..b745208cae 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class CompositeDbContext : DbContext +public sealed class CompositeDbContext : TestableDbContext { public DbSet Cars => Set(); public DbSet Engines => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PolicyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PolicyDbContext.cs index 61063269ce..2526faae2e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PolicyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PolicyDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class PolicyDbContext : DbContext +public sealed class PolicyDbContext : TestableDbContext { public DbSet Policies => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs index 63b748ffab..d0a6050a0b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ActionResultDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ControllerActionResults; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ActionResultDbContext : DbContext +public sealed class ActionResultDbContext : TestableDbContext { public DbSet Toothbrushes => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs index 784e07bb4b..9e2debb9a0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class CustomRouteDbContext : DbContext +public sealed class CustomRouteDbContext : TestableDbContext { public DbSet Towns => Set(); public DbSet Civilians => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs index 01f81cd319..030a1a447b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class EagerLoadingDbContext : DbContext +public sealed class EagerLoadingDbContext : TestableDbContext { public DbSet States => Set(); public DbSet Streets => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs index 141dfc4f71..bf176e681e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ErrorDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ErrorDbContext : DbContext +public sealed class ErrorDbContext : TestableDbContext { public DbSet ConsumerArticles => Set(); public DbSet ThrowingArticles => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingDbContext.cs index e92dae1318..a99db32f47 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class HostingDbContext : DbContext +public sealed class HostingDbContext : TestableDbContext { public DbSet ArtGalleries => Set(); public DbSet Paintings => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingStartup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingStartup.cs index a22dfe5954..1653cd5e96 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingStartup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/HostingStartup.cs @@ -1,14 +1,13 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class HostingStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscationDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscationDbContext.cs index 94921ea800..efc83bbdf8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscationDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/IdObfuscation/ObfuscationDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.IdObfuscation; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ObfuscationDbContext : DbContext +public sealed class ObfuscationDbContext : TestableDbContext { public DbSet BankAccounts => Set(); public DbSet DebitCards => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs index 2f1b56cf6f..73f3241f28 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ModelStateDbContext : DbContext +public sealed class ModelStateDbContext : TestableDbContext { public DbSet Volumes => Set(); public DbSet Directories => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowDbContext.cs index decc09bdc6..438093f855 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.RequestBody; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class WorkflowDbContext : DbContext +public sealed class WorkflowDbContext : TestableDbContext { public DbSet Workflows => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs index 390f8ec5e2..ffd2333fbe 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.Links; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class LinksDbContext : DbContext +public sealed class LinksDbContext : TestableDbContext { public DbSet PhotoAlbums => Set(); public DbSet Photos => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/LoggingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/LoggingDbContext.cs index 39c497616c..761806d3c9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/LoggingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/LoggingDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Logging; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class LoggingDbContext : DbContext +public sealed class LoggingDbContext : TestableDbContext { public DbSet AuditEntries => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaDbContext.cs index 25f6f10810..84937ff3d5 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/MetaDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Meta; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class MetaDbContext : DbContext +public sealed class MetaDbContext : TestableDbContext { public DbSet ProductFamilies => Set(); public DbSet SupportTickets => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/FireAndForgetDelivery/FireForgetDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/FireAndForgetDelivery/FireForgetDbContext.cs index 2dd337421a..83cddc52f0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/FireAndForgetDelivery/FireForgetDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/FireAndForgetDelivery/FireForgetDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices.FireAndForgetDelivery; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class FireForgetDbContext : DbContext +public sealed class FireForgetDbContext : TestableDbContext { public DbSet Users => Set(); public DbSet Groups => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/TransactionalOutboxPattern/OutboxDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/TransactionalOutboxPattern/OutboxDbContext.cs index f2a88e1c8d..c02b8fcc34 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/TransactionalOutboxPattern/OutboxDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/TransactionalOutboxPattern/OutboxDbContext.cs @@ -1,11 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCoreTests.IntegrationTests.Microservices.Messages; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices.TransactionalOutboxPattern; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class OutboxDbContext : DbContext +public sealed class OutboxDbContext : TestableDbContext { public DbSet Users => Set(); public DbSet Groups => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs index ce54a154d8..8e1fcd8350 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class MultiTenancyDbContext : DbContext +public sealed class MultiTenancyDbContext : TestableDbContext { private readonly ITenantProvider _tenantProvider; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs index cc0dfcd11d..da3d3b10f9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingConventionStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.NamingConventions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class KebabCasingConventionStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/NamingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/NamingDbContext.cs index d231a5f822..059abf5d51 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/NamingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/NamingDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.NamingConventions; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NamingDbContext : DbContext +public sealed class NamingDbContext : TestableDbContext { public DbSet SwimmingPools => Set(); public DbSet WaterSlides => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingConventionStartup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingConventionStartup.cs index c1c8cb8fa1..dad29067cf 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingConventionStartup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingConventionStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.NamingConventions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class PascalCasingConventionStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs index f4a24e1d00..0ac00ea2ae 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class EmptyDbContext : DbContext +public sealed class EmptyDbContext : TestableDbContext { public EmptyDbContext(DbContextOptions options) : base(options) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs index 39014e85b3..b18d71938c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class KnownDbContext : DbContext +public sealed class KnownDbContext : TestableDbContext { public DbSet KnownResources => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 1c6c7eff32..1b939ee9a1 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings.Filtering; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class FilterDbContext : DbContext +public sealed class FilterDbContext : TestableDbContext { public DbSet FilterableResources => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs index 1e240f8e8d..131bfe19fe 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class QueryStringDbContext : DbContext +public sealed class QueryStringDbContext : TestableDbContext { public DbSet Blogs => Set(); public DbSet Posts => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs index 276c3f0a91..d25acf8a06 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always // @formatter:keep_existing_linebreaks true @@ -7,7 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ReadWriteDbContext : DbContext +public sealed class ReadWriteDbContext : TestableDbContext { public DbSet WorkItems => Set(); public DbSet WorkTags => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs index 55ef653c95..6ed4deaeff 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class DefaultBehaviorDbContext : DbContext +public sealed class DefaultBehaviorDbContext : TestableDbContext { public DbSet Customers => Set(); public DbSet Orders => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs index faa496aef9..ea98dd6c08 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs @@ -2,11 +2,12 @@ using JsonApiDotNetCore; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class InjectionDbContext : DbContext +public sealed class InjectionDbContext : TestableDbContext { public ISystemClock SystemClock { get; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs index 7964bfdd09..3dc619dcba 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class UniverseDbContext : DbContext +public sealed class UniverseDbContext : TestableDbContext { public DbSet Stars => Set(); public DbSet Planets => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs index c8695f2ab4..7f62a63b73 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class SerializationDbContext : DbContext +public sealed class SerializationDbContext : TestableDbContext { public DbSet Students => Set(); public DbSet Scholarships => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceDbContext.cs index 23fa61b7ad..f5e0fc1bbe 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceDbContext.cs @@ -1,11 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance.Models; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public abstract class ResourceInheritanceDbContext : DbContext +public abstract class ResourceInheritanceDbContext : TestableDbContext { public DbSet Vehicles => Set(); public DbSet Bikes => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs index 8db6489aba..433d50ecd3 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/RestrictionDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class RestrictionDbContext : DbContext +public sealed class RestrictionDbContext : TestableDbContext { public DbSet Tables => Set
(); public DbSet Chairs => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/SerializationDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/SerializationDbContext.cs index 83c39f1791..885c3b950a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/SerializationDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/SerializationDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.IntegrationTests.Serialization; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class SerializationDbContext : DbContext +public sealed class SerializationDbContext : TestableDbContext { public DbSet Meetings => Set(); public DbSet Attendees => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs index e9a03df2f6..2a19ba74b4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class SoftDeletionDbContext : DbContext +public sealed class SoftDeletionDbContext : TestableDbContext { public DbSet Companies => Set(); public DbSet Departments => Set(); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs index bfe4fe7a3b..301a7b6d7b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs @@ -1,12 +1,13 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ZeroKeyDbContext : DbContext +public sealed class ZeroKeyDbContext : TestableDbContext { public DbSet Games => Set(); public DbSet Players => Set(); diff --git a/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs index 00af98c4e1..5e165653b3 100644 --- a/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksInApiNamespaceStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.Startups; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class AbsoluteLinksInApiNamespaceStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksNoNamespaceStartup.cs index 392c4ad8b2..c8234ed695 100644 --- a/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksNoNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreTests/Startups/AbsoluteLinksNoNamespaceStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.Startups; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class AbsoluteLinksNoNamespaceStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/Startups/NoModelStateValidationStartup.cs b/test/JsonApiDotNetCoreTests/Startups/NoModelStateValidationStartup.cs index e92b0301c7..ac6f9c82f1 100644 --- a/test/JsonApiDotNetCoreTests/Startups/NoModelStateValidationStartup.cs +++ b/test/JsonApiDotNetCoreTests/Startups/NoModelStateValidationStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.Startups; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class NoModelStateValidationStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/Startups/RelativeLinksInApiNamespaceStartup.cs b/test/JsonApiDotNetCoreTests/Startups/RelativeLinksInApiNamespaceStartup.cs index ff0d5b59b5..5fdfd20048 100644 --- a/test/JsonApiDotNetCoreTests/Startups/RelativeLinksInApiNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreTests/Startups/RelativeLinksInApiNamespaceStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.Startups; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class RelativeLinksInApiNamespaceStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/Startups/RelativeLinksNoNamespaceStartup.cs b/test/JsonApiDotNetCoreTests/Startups/RelativeLinksNoNamespaceStartup.cs index 658b874f08..99ae80d207 100644 --- a/test/JsonApiDotNetCoreTests/Startups/RelativeLinksNoNamespaceStartup.cs +++ b/test/JsonApiDotNetCoreTests/Startups/RelativeLinksNoNamespaceStartup.cs @@ -1,13 +1,12 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; namespace JsonApiDotNetCoreTests.Startups; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class RelativeLinksNoNamespaceStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs index d7d6484d2a..d0bb39a187 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs @@ -124,7 +124,7 @@ public CircularServiceB(CircularServiceA serviceA) } [UsedImplicitly(ImplicitUseTargetFlags.Members)] - private sealed class DependencyContainerRegistrationDbContext : DbContext + private sealed class DependencyContainerRegistrationDbContext : TestableDbContext { public DbSet Resources => Set(); diff --git a/test/TestBuildingBlocks/IntegrationTestContext.cs b/test/TestBuildingBlocks/IntegrationTestContext.cs index 8ccbc14079..bccf7d8bf3 100644 --- a/test/TestBuildingBlocks/IntegrationTestContext.cs +++ b/test/TestBuildingBlocks/IntegrationTestContext.cs @@ -26,7 +26,7 @@ namespace TestBuildingBlocks; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public class IntegrationTestContext : IntegrationTest, IDisposable where TStartup : class - where TDbContext : DbContext + where TDbContext : TestableDbContext { private readonly Lazy> _lazyFactory; private readonly TestControllerProvider _testControllerProvider = new(); diff --git a/test/TestBuildingBlocks/TestableDbContext.cs b/test/TestBuildingBlocks/TestableDbContext.cs new file mode 100644 index 0000000000..d40db11c03 --- /dev/null +++ b/test/TestBuildingBlocks/TestableDbContext.cs @@ -0,0 +1,20 @@ +using System.Diagnostics; +using JsonApiDotNetCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace TestBuildingBlocks; + +public abstract class TestableDbContext : DbContext +{ + protected TestableDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder builder) + { + // Writes SQL statements to the Output Window when debugging. + builder.LogTo(message => Debug.WriteLine(message), DbLoggerCategory.Database.Name.AsArray(), LogLevel.Information); + } +} diff --git a/test/TestBuildingBlocks/TestableStartup.cs b/test/TestBuildingBlocks/TestableStartup.cs index 687b9aa406..e5dc7075e8 100644 --- a/test/TestBuildingBlocks/TestableStartup.cs +++ b/test/TestBuildingBlocks/TestableStartup.cs @@ -1,12 +1,11 @@ using JsonApiDotNetCore.Configuration; using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace TestBuildingBlocks; public class TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { public virtual void ConfigureServices(IServiceCollection services) { diff --git a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs index 344668c800..a90c3851ee 100644 --- a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs @@ -12,6 +12,7 @@ using JsonApiDotNetCore.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; using Xunit; namespace UnitTests.Extensions; @@ -589,7 +590,7 @@ public void OnSerialize(ResourceOfGuid resource) } [UsedImplicitly(ImplicitUseTargetFlags.Members)] - private sealed class TestDbContext : DbContext + private sealed class TestDbContext : TestableDbContext { public DbSet ResourcesOfInt32 => Set(); public DbSet ResourcesOfGuid => Set(); From 2e4f15e07cbed92436b37fca20937ef103443c32 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 26 Oct 2022 23:14:28 +0200 Subject: [PATCH 26/47] Update guidance on one-to-one relationship mapping in EF Core to avoid common pitfalls --- docs/usage/resources/relationships.md | 119 +++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 11 deletions(-) diff --git a/docs/usage/resources/relationships.md b/docs/usage/resources/relationships.md index d1f36a89a9..1787bbd8ac 100644 --- a/docs/usage/resources/relationships.md +++ b/docs/usage/resources/relationships.md @@ -3,7 +3,7 @@ A relationship is a named link between two resource types, including a direction. They are similar to [navigation properties in Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/modeling/relationships). -Relationships come in three flavors: to-one, to-many and many-to-many. +Relationships come in two flavors: to-one and to-many. The left side of a relationship is where the relationship is declared, the right side is the resource type it points to. ## HasOne @@ -22,10 +22,14 @@ public class TodoItem : Identifiable The left side of this relationship is of type `TodoItem` (public name: "todoItems") and the right side is of type `Person` (public name: "persons"). -### Required one-to-one relationships in Entity Framework Core +### One-to-one relationships in Entity Framework Core -By default, Entity Framework Core generates an identifying foreign key for a required 1-to-1 relationship. -This means no foreign key column is generated, instead the primary keys point to each other directly. +By default, Entity Framework Core tries to generate an *identifying foreign key* for a one-to-one relationship whenever possible. +In that case, no foreign key column is generated. Instead the primary keys point to each other directly. + +**That mechanism does not make sense for JSON:API, because patching a relationship would result in also +changing the identity of a resource. Naming the foreign key explicitly fixes the problem, which enforces +to create a foreign key column.** The next example defines that each car requires an engine, while an engine is optionally linked to a car. @@ -51,18 +55,19 @@ public sealed class AppDbContext : DbContext builder.Entity() .HasOne(car => car.Engine) .WithOne(engine => engine.Car) - .HasForeignKey() - .IsRequired(); + .HasForeignKey(); } } ``` Which results in Entity Framework Core generating the next database objects: + ```sql CREATE TABLE "Engine" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, CONSTRAINT "PK_Engine" PRIMARY KEY ("Id") ); + CREATE TABLE "Cars" ( "Id" integer NOT NULL, CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"), @@ -71,9 +76,7 @@ CREATE TABLE "Cars" ( ); ``` -That mechanism does not make sense for JSON:API, because patching a relationship would result in also -changing the identity of a resource. Naming the foreign key explicitly fixes the problem by forcing to -create a foreign key column. +To fix this, name the foreign key explicitly: ```c# protected override void OnModelCreating(ModelBuilder builder) @@ -81,17 +84,18 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(car => car.Engine) .WithOne(engine => engine.Car) - .HasForeignKey("EngineId") // Explicit foreign key name added - .IsRequired(); + .HasForeignKey("EngineId"); // <-- Explicit foreign key name added } ``` Which generates the correct database objects: + ```sql CREATE TABLE "Engine" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, CONSTRAINT "PK_Engine" PRIMARY KEY ("Id") ); + CREATE TABLE "Cars" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "EngineId" integer NOT NULL, @@ -99,6 +103,99 @@ CREATE TABLE "Cars" ( CONSTRAINT "FK_Cars_Engine_EngineId" FOREIGN KEY ("EngineId") REFERENCES "Engine" ("Id") ON DELETE CASCADE ); + +CREATE UNIQUE INDEX "IX_Cars_EngineId" ON "Cars" ("EngineId"); +``` + +#### Optional one-to-one relationships in Entity Framework Core + +For optional one-to-one relationships, Entity Framework Core uses `DeleteBehavior.ClientSetNull` by default, instead of `DeleteBehavior.SetNull`. +This means that Entity Framework Core tries to handle the cascading effects (by sending multiple SQL statements), instead of leaving it up to the database. +Of course that's only going to work when all the related resources are loaded in the change tracker upfront, which is expensive because it requires fetching more data than necessary. + +The reason for this odd default is poor support in SQL Server, as explained [here](https://stackoverflow.com/questions/54326165/ef-core-why-clientsetnull-is-default-ondelete-behavior-for-optional-relations) and [here](https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete#database-cascade-limitations). + +**Our [testing](https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1205) shows that these limitations don't exist when using PostgreSQL. +Therefore the general advice is to map the delete behavior of optional one-to-one relationships explicitly with `.OnDelete(DeleteBehavior.SetNull)`. This is simpler and more efficient.** + +The next example defines that each car optionally has an engine, while an engine is optionally linked to a car. + +```c# +#nullable enable + +public sealed class Car : Identifiable +{ + [HasOne] + public Engine? Engine { get; set; } +} + +public sealed class Engine : Identifiable +{ + [HasOne] + public Car? Car { get; set; } +} + +public sealed class AppDbContext : DbContext +{ + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(car => car.Engine) + .WithOne(engine => engine.Car) + .HasForeignKey("EngineId"); + } +} +``` + +Which results in Entity Framework Core generating the next database objects: + +```sql +CREATE TABLE "Engines" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + CONSTRAINT "PK_Engines" PRIMARY KEY ("Id") +); + +CREATE TABLE "Cars" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "EngineId" integer NULL, + CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Cars_Engines_EngineId" FOREIGN KEY ("EngineId") REFERENCES "Engines" ("Id") +); + +CREATE UNIQUE INDEX "IX_Cars_EngineId" ON "Cars" ("EngineId"); +``` + +To fix this, set the delete behavior explicitly: + +``` +public sealed class AppDbContext : DbContext +{ + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(car => car.Engine) + .WithOne(engine => engine.Car) + .HasForeignKey("EngineId") + .OnDelete(DeleteBehavior.SetNull); // <-- Explicit delete behavior set + } +} +``` + +Which generates the correct database objects: + +```sql +CREATE TABLE "Engines" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + CONSTRAINT "PK_Engines" PRIMARY KEY ("Id") +); + +CREATE TABLE "Cars" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "EngineId" integer NULL, + CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Cars_Engines_EngineId" FOREIGN KEY ("EngineId") REFERENCES "Engines" ("Id") ON DELETE SET NULL +); + CREATE UNIQUE INDEX "IX_Cars_EngineId" ON "Cars" ("EngineId"); ``` From bbec95bc29760c878463bc9e19ac89871451041a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 26 Oct 2022 23:14:52 +0200 Subject: [PATCH 27/47] Fix naming inconsistency --- .../InjectionDbContext.cs | 2 +- .../ResourceInjectionTests.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs index faa496aef9..1eade6b66b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/InjectionDbContext.cs @@ -10,7 +10,7 @@ public sealed class InjectionDbContext : DbContext { public ISystemClock SystemClock { get; } - public DbSet PostOffice => Set(); + public DbSet PostOffices => Set(); public DbSet GiftCertificates => Set(); public InjectionDbContext(DbContextOptions options, ISystemClock systemClock) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs index 68e5c1a77a..19b38cf071 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/ResourceInjectionTests.cs @@ -72,7 +72,7 @@ public async Task Can_filter_resources_by_ID() await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync(); - dbContext.PostOffice.AddRange(postOffices); + dbContext.PostOffices.AddRange(postOffices); await dbContext.SaveChangesAsync(); }); @@ -133,7 +133,7 @@ public async Task Can_create_resource_with_ToOne_relationship_and_include() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.PostOffice.Add(existingOffice); + dbContext.PostOffices.Add(existingOffice); await dbContext.SaveChangesAsync(); }); @@ -216,7 +216,7 @@ public async Task Can_update_resource_with_ToMany_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.PostOffice.Add(existingOffice); + dbContext.PostOffices.Add(existingOffice); await dbContext.SaveChangesAsync(); }); @@ -259,7 +259,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - PostOffice officeInDatabase = await dbContext.PostOffice.Include(postOffice => postOffice.GiftCertificates).FirstWithIdAsync(existingOffice.Id); + PostOffice officeInDatabase = await dbContext.PostOffices.Include(postOffice => postOffice.GiftCertificates).FirstWithIdAsync(existingOffice.Id); officeInDatabase.Address.Should().Be(newAddress); @@ -276,7 +276,7 @@ public async Task Can_delete_resource() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.PostOffice.Add(existingOffice); + dbContext.PostOffices.Add(existingOffice); await dbContext.SaveChangesAsync(); }); @@ -292,7 +292,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - PostOffice? officeInDatabase = await dbContext.PostOffice.FirstWithIdOrDefaultAsync(existingOffice.Id); + PostOffice? officeInDatabase = await dbContext.PostOffices.FirstWithIdOrDefaultAsync(existingOffice.Id); officeInDatabase.Should().BeNull(); }); @@ -359,7 +359,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - PostOffice officeInDatabase = await dbContext.PostOffice.Include(postOffice => postOffice.GiftCertificates).FirstWithIdAsync(existingOffice.Id); + PostOffice officeInDatabase = await dbContext.PostOffices.Include(postOffice => postOffice.GiftCertificates).FirstWithIdAsync(existingOffice.Id); officeInDatabase.GiftCertificates.ShouldHaveCount(2); }); From 75fad29cfa38f29fadfc1b70a2abb4e4fd326ca7 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 27 Oct 2022 12:37:40 +0200 Subject: [PATCH 28/47] Updated example to match with current implementation --- docs/internals/queries.md | 168 ++++++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 54 deletions(-) diff --git a/docs/internals/queries.md b/docs/internals/queries.md index 46005f489c..5c2b238a18 100644 --- a/docs/internals/queries.md +++ b/docs/internals/queries.md @@ -22,7 +22,7 @@ Processing a request involves the following steps: - `JsonApiResourceService` contains no more usage of `IQueryable`. - `EntityFrameworkCoreRepository` delegates to `QueryableBuilder` to transform the `QueryLayer` tree into `IQueryable` expression trees. `QueryBuilder` depends on `QueryClauseBuilder` implementations that visit the tree nodes, transforming them to `System.Linq.Expression` equivalents. - The `IQueryable` expression trees are executed by Entity Framework Core, which produces SQL statements out of them. + The `IQueryable` expression trees are passed to Entity Framework Core, which produces SQL statements out of them. - `JsonApiWriter` transforms resource objects into json response. # Example @@ -30,17 +30,17 @@ To get a sense of what this all looks like, let's look at an example query strin ``` /api/v1/blogs? - include=owner,articles.revisions.author& - filter=has(articles)& - sort=count(articles)& + include=owner,posts.comments.author& + filter=has(posts)& + sort=count(posts)& page[number]=3& fields[blogs]=title& - filter[articles]=and(not(equals(author.firstName,null)),has(revisions))& - sort[articles]=author.lastName& - fields[articles]=url& - filter[articles.revisions]=and(greaterThan(publishTime,'2001-01-01'),startsWith(author.firstName,'J'))& - sort[articles.revisions]=-publishTime,author.lastName& - fields[revisions]=publishTime + filter[posts]=and(not(equals(author.userName,null)),has(comments))& + sort[posts]=author.displayName& + fields[blogPosts]=url& + filter[posts.comments]=and(greaterThan(createdAt,'2001-01-01Z'),startsWith(author.userName,'J'))& + sort[posts.comments]=-createdAt,author.displayName& + fields[comments]=createdAt ``` After parsing, the set of scoped expressions is transformed into the following tree by `QueryLayerComposer`: @@ -48,40 +48,50 @@ After parsing, the set of scoped expressions is transformed into the following t ``` QueryLayer { - Include: owner,articles.revisions - Filter: has(articles) - Sort: count(articles) + Include: owner,posts.comments.author + Filter: has(posts) + Sort: count(posts) Pagination: Page number: 3, size: 5 - Projection + Selection { - title - id - owner: QueryLayer + FieldSelectors { - Sort: id - Pagination: Page number: 1, size: 5 - } - articles: QueryLayer
- { - Filter: and(not(equals(author.firstName,null)),has(revisions)) - Sort: author.lastName - Pagination: Page number: 1, size: 5 - Projection + title + id + posts: QueryLayer { - url - id - revisions: QueryLayer + Filter: and(not(equals(author.userName,null)),has(comments)) + Sort: author.displayName + Pagination: Page number: 1, size: 5 + Selection { - Filter: and(greaterThan(publishTime,'2001-01-01'),startsWith(author.firstName,'J')) - Sort: -publishTime,author.lastName - Pagination: Page number: 1, size: 5 - Projection + FieldSelectors { - publishTime + url id + comments: QueryLayer + { + Filter: and(greaterThan(createdAt,'2001-01-01'),startsWith(author.userName,'J')) + Sort: -createdAt,author.displayName + Pagination: Page number: 1, size: 5 + Selection + { + FieldSelectors + { + createdAt + id + author: QueryLayer + { + } + } + } + } } } } + owner: QueryLayer + { + } } } } @@ -90,36 +100,86 @@ QueryLayer Next, the repository translates this into a LINQ query that the following C# code would represent: ```c# -var query = dbContext.Blogs +IQueryable query = dbContext.Blogs + .Include("Posts.Comments.Author") .Include("Owner") - .Include("Articles.Revisions") - .Where(blog => blog.Articles.Any()) - .OrderBy(blog => blog.Articles.Count) + .Where(blog => blog.Posts.Any()) + .OrderBy(blog => blog.Posts.Count) .Skip(10) .Take(5) .Select(blog => new Blog { Title = blog.Title, Id = blog.Id, - Owner = blog.Owner, - Articles = new List
(blog.Articles - .Where(article => article.Author.FirstName != null && article.Revisions.Any()) - .OrderBy(article => article.Author.LastName) + Posts = blog.Posts + .Where(blogPost => blogPost.Author.UserName != null && blogPost.Comments.Any()) + .OrderBy(blogPost => blogPost.Author.DisplayName) .Take(5) - .Select(article => new Article + .Select(blogPost => new BlogPost { - Url = article.Url, - Id = article.Id, - Revisions = new HashSet(article.Revisions - .Where(revision => revision.PublishTime > DateTime.Parse("2001-01-01") && revision.Author.FirstName.StartsWith("J")) - .OrderByDescending(revision => revision.PublishTime) - .ThenBy(revision => revision.Author.LastName) + Url = blogPost.Url, + Id = blogPost.Id, + Comments = blogPost.Comments + .Where(comment => comment.CreatedAt > DateTime.Parse("2001-01-01Z") && + comment.Author.UserName.StartsWith("J")) + .OrderByDescending(comment => comment.CreatedAt) + .ThenBy(comment => comment.Author.DisplayName) .Take(5) - .Select(revision => new Revision + .Select(comment => new Comment { - PublishTime = revision.PublishTime, - Id = revision.Id - })) - })) + CreatedAt = comment.CreatedAt, + Id = comment.Id, + Author = comment.Author + }).ToHashSet() + }).ToList(), + Owner = blog.Owner }); ``` + +The LINQ query gets translated by Entity Framework Core into the following SQL: + +```sql +SELECT t."Title", t."Id", a."Id", t2."Url", t2."Id", t2."Id0", t2."CreatedAt", t2."Id1", t2."Id00", t2."DateOfBirth", t2."DisplayName", t2."EmailAddress", t2."Password", t2."PersonId", t2."PreferencesId", t2."UserName", a."DateOfBirth", a."DisplayName", a."EmailAddress", a."Password", a."PersonId", a."PreferencesId", a."UserName" +FROM ( + SELECT b."Id", b."OwnerId", b."Title", ( + SELECT COUNT(*)::INT + FROM "Posts" AS p0 + WHERE b."Id" = p0."ParentId") AS c + FROM "Blogs" AS b + WHERE EXISTS ( + SELECT 1 + FROM "Posts" AS p + WHERE b."Id" = p."ParentId") + ORDER BY ( + SELECT COUNT(*)::INT + FROM "Posts" AS p0 + WHERE b."Id" = p0."ParentId") + LIMIT @__Create_Item1_1 OFFSET @__Create_Item1_0 +) AS t +LEFT JOIN "Accounts" AS a ON t."OwnerId" = a."Id" +LEFT JOIN LATERAL ( + SELECT t0."Url", t0."Id", t0."Id0", t1."CreatedAt", t1."Id" AS "Id1", t1."Id0" AS "Id00", t1."DateOfBirth", t1."DisplayName", t1."EmailAddress", t1."Password", t1."PersonId", t1."PreferencesId", t1."UserName", t0."DisplayName" AS "DisplayName0", t1."ParentId" + FROM ( + SELECT p1."Url", p1."Id", a0."Id" AS "Id0", a0."DisplayName" + FROM "Posts" AS p1 + LEFT JOIN "Accounts" AS a0 ON p1."AuthorId" = a0."Id" + WHERE (t."Id" = p1."ParentId") AND (((a0."UserName" IS NOT NULL)) AND EXISTS ( + SELECT 1 + FROM "Comments" AS c + WHERE p1."Id" = c."ParentId")) + ORDER BY a0."DisplayName" + LIMIT @__Create_Item1_1 + ) AS t0 + LEFT JOIN ( + SELECT t3."CreatedAt", t3."Id", t3."Id0", t3."DateOfBirth", t3."DisplayName", t3."EmailAddress", t3."Password", t3."PersonId", t3."PreferencesId", t3."UserName", t3."ParentId" + FROM ( + SELECT c0."CreatedAt", c0."Id", a1."Id" AS "Id0", a1."DateOfBirth", a1."DisplayName", a1."EmailAddress", a1."Password", a1."PersonId", a1."PreferencesId", a1."UserName", c0."ParentId", ROW_NUMBER() OVER(PARTITION BY c0."ParentId" ORDER BY c0."CreatedAt" DESC, a1."DisplayName") AS row + FROM "Comments" AS c0 + LEFT JOIN "Accounts" AS a1 ON c0."AuthorId" = a1."Id" + WHERE (c0."CreatedAt" > @__Create_Item1_2) AND ((@__Create_Item1_3 = '') OR (((a1."UserName" IS NOT NULL)) AND ((a1."UserName" LIKE @__Create_Item1_3 || '%' ESCAPE '') AND (left(a1."UserName", length(@__Create_Item1_3))::text = @__Create_Item1_3::text)))) + ) AS t3 + WHERE t3.row <= @__Create_Item1_1 + ) AS t1 ON t0."Id" = t1."ParentId" +) AS t2 ON TRUE +ORDER BY t.c, t."Id", a."Id", t2."DisplayName0", t2."Id", t2."Id0", t2."ParentId", t2."CreatedAt" DESC, t2."DisplayName", t2."Id1" +``` From 13a47d290bdbd851d6510198bb2d28270a7f5e87 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 8 Nov 2022 21:59:52 +0100 Subject: [PATCH 29/47] Update version to 5.1.0 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3a3d018a65..99319a64e6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ 6.0.* 4.3.* 2.14.1 - 5.0.4 + 5.1.0 $(MSBuildThisFileDirectory)CodingGuidelines.ruleset 9999 enable From ceb27c36a99ea953ff7f7cbbd03532216775a605 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 8 Nov 2022 22:12:14 +0100 Subject: [PATCH 30/47] Increment version to 5.1.1 (used for pre-release builds from ci) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 99319a64e6..8e68ea532b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ 6.0.* 4.3.* 2.14.1 - 5.1.0 + 5.1.1 $(MSBuildThisFileDirectory)CodingGuidelines.ruleset 9999 enable From f476ce29199a5642b71dd28c586ff1231658cb62 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 9 Nov 2022 00:38:03 +0100 Subject: [PATCH 31/47] Post-merge fixes --- .../ArgumentGuard.cs | 9 ++++----- .../JsonApiClient.cs | 20 +++++++++---------- .../JsonApiDotNetCore.OpenApi.Client.csproj | 6 ++---- .../ActionDescriptorExtensions.cs | 6 +++--- ...onApiActionDescriptorCollectionProvider.cs | 4 ++-- .../JsonApiDotNetCore.OpenApi.csproj | 6 ++---- .../JsonApiMetadata/EndpointResolver.cs | 2 +- .../JsonApiEndpointMetadataProvider.cs | 4 ++-- .../NonPrimaryEndpointMetadata.cs | 2 +- .../JsonApiMetadata/PrimaryRequestMetadata.cs | 2 +- .../PrimaryResponseMetadata.cs | 2 +- .../NonPrimaryDocumentTypeFactory.cs | 12 +++++------ .../JsonApiObjects/RelationshipTypeFactory.cs | 4 ++-- .../JsonApiOperationIdSelector.cs | 4 ++-- .../JsonApiRequestFormatMetadataProvider.cs | 4 ++-- .../JsonApiSchemaIdSelector.cs | 4 ++-- .../MemberInfoExtensions.cs | 2 +- .../ObjectExtensions.cs | 2 +- .../OpenApiEndpointConvention.cs | 4 ++-- .../ParameterInfoExtensions.cs | 8 ++++---- .../ServiceCollectionExtensions.cs | 4 ++-- .../CachingSwaggerGenerator.cs | 4 ++-- .../EndpointOrderingFilter.cs | 4 ++-- .../JsonApiDataContractResolver.cs | 6 +++--- .../JsonApiSchemaGenerator.cs | 10 +++++----- .../NullableReferenceSchemaGenerator.cs | 4 ++-- .../ResourceFieldObjectSchemaBuilder.cs | 8 ++++---- .../ResourceObjectSchemaGenerator.cs | 10 +++++----- .../SwaggerComponents/ResourceTypeInfo.cs | 4 ++-- .../ResourceTypeSchemaGenerator.cs | 6 +++--- .../SchemaRepositoryAccessor.cs | 2 +- .../LegacyClient/ApiResponse.cs | 2 +- .../LegacyClient/FakeHttpClientWrapper.cs | 2 +- .../LegacyIntegrationDbContext.cs | 3 ++- .../LegacyOpenApiIntegrationStartup.cs | 4 ++-- .../CamelCaseNamingConventionStartup.cs | 4 ++-- .../KebabCaseNamingConventionStartup.cs | 4 ++-- .../NamingConventionsDbContext.cs | 3 ++- .../PascalCaseNamingConventionStartup.cs | 4 ++-- test/OpenApiTests/OpenApiStartup.cs | 3 +-- test/OpenApiTests/OpenApiTestContext.cs | 5 ++--- 41 files changed, 99 insertions(+), 104 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/ArgumentGuard.cs b/src/JsonApiDotNetCore.OpenApi.Client/ArgumentGuard.cs index da71b2c8e5..71a217a9b3 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/ArgumentGuard.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/ArgumentGuard.cs @@ -1,19 +1,18 @@ +using System.Runtime.CompilerServices; using JetBrains.Annotations; using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; #pragma warning disable AV1008 // Class should not be static +#pragma warning disable AV1553 // Do not use optional parameters with default value null for strings, collections or tasks namespace JsonApiDotNetCore.OpenApi.Client; internal static class ArgumentGuard { [AssertionMethod] - public static void NotNull([NoEnumeration] [SysNotNull] T? value, [InvokerParameterName] string name) + public static void NotNull([NoEnumeration] [SysNotNull] T? value, [CallerArgumentExpression("value")] string? parameterName = null) where T : class { - if (value is null) - { - throw new ArgumentNullException(name); - } + ArgumentNullException.ThrowIfNull(value, parameterName); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 7a1ba5883d..219ac04353 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -17,7 +17,7 @@ public abstract class JsonApiClient : IJsonApiClient protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings) { - ArgumentGuard.NotNull(settings, nameof(settings)); + ArgumentGuard.NotNull(settings); settings.Converters.Add(_jsonApiJsonConverter); } @@ -27,7 +27,7 @@ public IDisposable RegisterAttributesForRequestDocument>[] alwaysIncludedAttributeSelectors) where TRequestDocument : class { - ArgumentGuard.NotNull(requestDocument, nameof(requestDocument)); + ArgumentGuard.NotNull(requestDocument); var attributeNames = new HashSet(); @@ -105,7 +105,7 @@ public void RemoveAttributeRegistration(object requestDocument) public override bool CanConvert(Type objectType) { - ArgumentGuard.NotNull(objectType, nameof(objectType)); + ArgumentGuard.NotNull(objectType); return !_isSerializing && _requestDocumentInstancesPerRequestDocumentType.ContainsKey(objectType); } @@ -117,8 +117,8 @@ public override object ReadJson(JsonReader reader, Type objectType, object? exis public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - ArgumentGuard.NotNull(writer, nameof(writer)); - ArgumentGuard.NotNull(serializer, nameof(serializer)); + ArgumentGuard.NotNull(writer); + ArgumentGuard.NotNull(serializer); if (value != null) { @@ -148,8 +148,8 @@ private sealed class AttributeNamesContainer public AttributeNamesContainer(ISet attributeNames, Type containerType) { - ArgumentGuard.NotNull(attributeNames, nameof(attributeNames)); - ArgumentGuard.NotNull(containerType, nameof(containerType)); + ArgumentGuard.NotNull(attributeNames); + ArgumentGuard.NotNull(containerType); _attributeNames = attributeNames; _containerType = containerType; @@ -173,8 +173,8 @@ private sealed class AttributesRegistrationScope : IDisposable public AttributesRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, object requestDocument) { - ArgumentGuard.NotNull(jsonApiJsonConverter, nameof(jsonApiJsonConverter)); - ArgumentGuard.NotNull(requestDocument, nameof(requestDocument)); + ArgumentGuard.NotNull(jsonApiJsonConverter); + ArgumentGuard.NotNull(requestDocument); _jsonApiJsonConverter = jsonApiJsonConverter; _requestDocument = requestDocument; @@ -192,7 +192,7 @@ private sealed class JsonApiDocumentContractResolver : DefaultContractResolver public JsonApiDocumentContractResolver(AttributeNamesContainer attributeNamesContainer) { - ArgumentGuard.NotNull(attributeNamesContainer, nameof(attributeNamesContainer)); + ArgumentGuard.NotNull(attributeNamesContainer); _attributeNamesContainer = attributeNamesContainer; } diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiDotNetCore.OpenApi.Client.csproj b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiDotNetCore.OpenApi.Client.csproj index 22bbc3665a..7246bf0781 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiDotNetCore.OpenApi.Client.csproj +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiDotNetCore.OpenApi.Client.csproj @@ -28,10 +28,8 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs index 4ace6ada42..9f83e18031 100644 --- a/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs @@ -10,7 +10,7 @@ internal static class ActionDescriptorExtensions { public static MethodInfo GetActionMethod(this ActionDescriptor descriptor) { - ArgumentGuard.NotNull(descriptor, nameof(descriptor)); + ArgumentGuard.NotNull(descriptor); return ((ControllerActionDescriptor)descriptor).MethodInfo; } @@ -18,7 +18,7 @@ public static MethodInfo GetActionMethod(this ActionDescriptor descriptor) public static TFilterMetaData? GetFilterMetadata(this ActionDescriptor descriptor) where TFilterMetaData : IFilterMetadata { - ArgumentGuard.NotNull(descriptor, nameof(descriptor)); + ArgumentGuard.NotNull(descriptor); IFilterMetadata? filterMetadata = descriptor.FilterDescriptors.Select(filterDescriptor => filterDescriptor.Filter) .FirstOrDefault(filter => filter is TFilterMetaData); @@ -28,7 +28,7 @@ public static MethodInfo GetActionMethod(this ActionDescriptor descriptor) public static ControllerParameterDescriptor? GetBodyParameterDescriptor(this ActionDescriptor descriptor) { - ArgumentGuard.NotNull(descriptor, nameof(descriptor)); + ArgumentGuard.NotNull(descriptor); return (ControllerParameterDescriptor?)descriptor.Parameters.FirstOrDefault(parameterDescriptor => // ReSharper disable once ConstantConditionalAccessQualifier diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs index dca0b5aef7..98ae9938b4 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs @@ -24,8 +24,8 @@ internal sealed class JsonApiActionDescriptorCollectionProvider : IActionDescrip public JsonApiActionDescriptorCollectionProvider(IControllerResourceMapping controllerResourceMapping, IActionDescriptorCollectionProvider defaultProvider) { - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); - ArgumentGuard.NotNull(defaultProvider, nameof(defaultProvider)); + ArgumentGuard.NotNull(controllerResourceMapping); + ArgumentGuard.NotNull(defaultProvider); _defaultProvider = defaultProvider; _jsonApiEndpointMetadataProvider = new JsonApiEndpointMetadataProvider(controllerResourceMapping); diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiDotNetCore.OpenApi.csproj b/src/JsonApiDotNetCore.OpenApi/JsonApiDotNetCore.OpenApi.csproj index 95e9b33f7b..d2ad102392 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiDotNetCore.OpenApi.csproj +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiDotNetCore.OpenApi.csproj @@ -32,10 +32,8 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs index bbe2e35ba0..38b3b6677e 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/EndpointResolver.cs @@ -9,7 +9,7 @@ internal sealed class EndpointResolver { public JsonApiEndpoint? Get(MethodInfo controllerAction) { - ArgumentGuard.NotNull(controllerAction, nameof(controllerAction)); + ArgumentGuard.NotNull(controllerAction); // This is a temporary work-around to prevent the JsonApiDotNetCoreExample project from crashing upon startup. if (!IsJsonApiController(controllerAction) || IsOperationsController(controllerAction)) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs index bc23d3e0ec..d0fe2f1234 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs @@ -18,14 +18,14 @@ internal sealed class JsonApiEndpointMetadataProvider public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping) { - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); + ArgumentGuard.NotNull(controllerResourceMapping); _controllerResourceMapping = controllerResourceMapping; } public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction) { - ArgumentGuard.NotNull(controllerAction, nameof(controllerAction)); + ArgumentGuard.NotNull(controllerAction); JsonApiEndpoint? endpoint = _endpointResolver.Get(controllerAction); diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryEndpointMetadata.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryEndpointMetadata.cs index 650108b9f9..72cb2c0c12 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryEndpointMetadata.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryEndpointMetadata.cs @@ -6,7 +6,7 @@ internal abstract class NonPrimaryEndpointMetadata protected NonPrimaryEndpointMetadata(IDictionary documentTypesByRelationshipName) { - ArgumentGuard.NotNull(documentTypesByRelationshipName, nameof(documentTypesByRelationshipName)); + ArgumentGuard.NotNull(documentTypesByRelationshipName); DocumentTypesByRelationshipName = documentTypesByRelationshipName; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryRequestMetadata.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryRequestMetadata.cs index 8497de020a..f5784b0177 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryRequestMetadata.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryRequestMetadata.cs @@ -6,7 +6,7 @@ internal sealed class PrimaryRequestMetadata : IJsonApiRequestMetadata public PrimaryRequestMetadata(Type documentType) { - ArgumentGuard.NotNull(documentType, nameof(documentType)); + ArgumentGuard.NotNull(documentType); DocumentType = documentType; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryResponseMetadata.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryResponseMetadata.cs index dc02f69f7b..914191a0c1 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryResponseMetadata.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/PrimaryResponseMetadata.cs @@ -6,7 +6,7 @@ internal sealed class PrimaryResponseMetadata : IJsonApiResponseMetadata public PrimaryResponseMetadata(Type documentType) { - ArgumentGuard.NotNull(documentType, nameof(documentType)); + ArgumentGuard.NotNull(documentType); DocumentType = documentType; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs index 6ec4dbce2a..95d7e2f6e5 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs @@ -23,21 +23,21 @@ private NonPrimaryDocumentTypeFactory() public Type GetForSecondaryResponse(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); return Get(relationship, SecondaryResponseDocumentOpenTypes); } public Type GetForRelationshipRequest(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); return Get(relationship, RelationshipRequestDocumentOpenTypes); } public Type GetForRelationshipResponse(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); return Get(relationship, RelationshipResponseDocumentOpenTypes); } @@ -65,9 +65,9 @@ private sealed class DocumentOpenTypes public DocumentOpenTypes(Type manyDataOpenType, Type nullableSingleDataOpenType, Type singleDataOpenType) { - ArgumentGuard.NotNull(manyDataOpenType, nameof(manyDataOpenType)); - ArgumentGuard.NotNull(nullableSingleDataOpenType, nameof(nullableSingleDataOpenType)); - ArgumentGuard.NotNull(singleDataOpenType, nameof(singleDataOpenType)); + ArgumentGuard.NotNull(manyDataOpenType); + ArgumentGuard.NotNull(nullableSingleDataOpenType); + ArgumentGuard.NotNull(singleDataOpenType); ManyDataOpenType = manyDataOpenType; NullableSingleDataOpenType = nullableSingleDataOpenType; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs index 28bb85d83e..7593c9f9f5 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs @@ -13,14 +13,14 @@ private RelationshipTypeFactory() public Type GetForRequest(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); return NonPrimaryDocumentTypeFactory.Instance.GetForRelationshipRequest(relationship); } public Type GetForResponse(RelationshipAttribute relationship) { - ArgumentGuard.NotNull(relationship, nameof(relationship)); + ArgumentGuard.NotNull(relationship); // @formatter:nested_ternary_style expanded diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs index 9724effbd5..197d1d27b8 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs @@ -39,7 +39,7 @@ internal sealed class JsonApiOperationIdSelector public JsonApiOperationIdSelector(IControllerResourceMapping controllerResourceMapping, JsonNamingPolicy? namingPolicy) { - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); + ArgumentGuard.NotNull(controllerResourceMapping); _controllerResourceMapping = controllerResourceMapping; _namingPolicy = namingPolicy; @@ -47,7 +47,7 @@ public JsonApiOperationIdSelector(IControllerResourceMapping controllerResourceM public string GetOperationId(ApiDescription endpoint) { - ArgumentGuard.NotNull(endpoint, nameof(endpoint)); + ArgumentGuard.NotNull(endpoint); ResourceType? primaryResourceType = _controllerResourceMapping.GetResourceTypeForController(endpoint.ActionDescriptor.GetActionMethod().ReflectedType); diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiRequestFormatMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiRequestFormatMetadataProvider.cs index 26dcac4c48..297f23a613 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiRequestFormatMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiRequestFormatMetadataProvider.cs @@ -33,8 +33,8 @@ public Task ReadAsync(InputFormatterContext context) /// public IReadOnlyList GetSupportedContentTypes(string contentType, Type objectType) { - ArgumentGuard.NotNullNorEmpty(contentType, nameof(contentType)); - ArgumentGuard.NotNull(objectType, nameof(objectType)); + ArgumentGuard.NotNullNorEmpty(contentType); + ArgumentGuard.NotNull(objectType); if (contentType == HeaderConstants.MediaType && objectType.IsGenericType && JsonApiRequestObjectOpenType.Contains(objectType.GetGenericTypeDefinition())) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs index 2eb3f13824..c62bf662ff 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs @@ -43,7 +43,7 @@ internal sealed class JsonApiSchemaIdSelector public JsonApiSchemaIdSelector(JsonNamingPolicy? namingPolicy, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(resourceGraph); _namingPolicy = namingPolicy; _resourceGraph = resourceGraph; @@ -51,7 +51,7 @@ public JsonApiSchemaIdSelector(JsonNamingPolicy? namingPolicy, IResourceGraph re public string GetSchemaId(Type type) { - ArgumentGuard.NotNull(type, nameof(type)); + ArgumentGuard.NotNull(type); ResourceType? resourceType = _resourceGraph.FindResourceType(type); diff --git a/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs b/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs index a6f46ccb9d..f13c91e837 100644 --- a/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs @@ -7,7 +7,7 @@ internal static class MemberInfoExtensions { public static TypeCategory GetTypeCategory(this MemberInfo source) { - ArgumentGuard.NotNull(source, nameof(source)); + ArgumentGuard.NotNull(source); Type memberType; diff --git a/src/JsonApiDotNetCore.OpenApi/ObjectExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ObjectExtensions.cs index 2047b11f96..8c216c93df 100644 --- a/src/JsonApiDotNetCore.OpenApi/ObjectExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ObjectExtensions.cs @@ -10,7 +10,7 @@ internal static class ObjectExtensions public static object MemberwiseClone(this object source) { - ArgumentGuard.NotNull(source, nameof(source)); + ArgumentGuard.NotNull(source); return MemberwiseCloneMethod.Value.Invoke(source, null)!; } diff --git a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs index 6f175b71d5..c83266c1b0 100644 --- a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs +++ b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs @@ -18,14 +18,14 @@ internal sealed class OpenApiEndpointConvention : IActionModelConvention public OpenApiEndpointConvention(IControllerResourceMapping controllerResourceMapping) { - ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping)); + ArgumentGuard.NotNull(controllerResourceMapping); _controllerResourceMapping = controllerResourceMapping; } public void Apply(ActionModel action) { - ArgumentGuard.NotNull(action, nameof(action)); + ArgumentGuard.NotNull(action); JsonApiEndpoint? endpoint = _endpointResolver.Get(action.ActionMethod); diff --git a/src/JsonApiDotNetCore.OpenApi/ParameterInfoExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ParameterInfoExtensions.cs index ea14dce0b8..2a0b94f103 100644 --- a/src/JsonApiDotNetCore.OpenApi/ParameterInfoExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ParameterInfoExtensions.cs @@ -12,8 +12,8 @@ internal static class ParameterInfoExtensions public static ParameterInfo WithName(this ParameterInfo source, string name) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNullNorEmpty(name, nameof(name)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNullNorEmpty(name); var cloned = (ParameterInfo)source.MemberwiseClone(); NameField.Value.SetValue(cloned, name); @@ -23,8 +23,8 @@ public static ParameterInfo WithName(this ParameterInfo source, string name) public static ParameterInfo WithParameterType(this ParameterInfo source, Type parameterType) { - ArgumentGuard.NotNull(source, nameof(source)); - ArgumentGuard.NotNull(parameterType, nameof(parameterType)); + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(parameterType); var cloned = (ParameterInfo)source.MemberwiseClone(); ParameterTypeField.Value.SetValue(cloned, parameterType); diff --git a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs index 55695102a2..f223c7d994 100644 --- a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs @@ -18,8 +18,8 @@ public static class ServiceCollectionExtensions /// public static void AddOpenApi(this IServiceCollection services, IMvcCoreBuilder mvcBuilder, Action? setupSwaggerGenAction = null) { - ArgumentGuard.NotNull(services, nameof(services)); - ArgumentGuard.NotNull(mvcBuilder, nameof(mvcBuilder)); + ArgumentGuard.NotNull(services); + ArgumentGuard.NotNull(mvcBuilder); AddCustomApiExplorer(services, mvcBuilder); diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs index 26f5fab3f8..f3867e4dc2 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs @@ -18,13 +18,13 @@ internal sealed class CachingSwaggerGenerator : ISwaggerProvider public CachingSwaggerGenerator(SwaggerGenerator defaultSwaggerGenerator) { - ArgumentGuard.NotNull(defaultSwaggerGenerator, nameof(defaultSwaggerGenerator)); + ArgumentGuard.NotNull(defaultSwaggerGenerator); _defaultSwaggerGenerator = defaultSwaggerGenerator; } public OpenApiDocument GetSwagger(string documentName, string? host = null, string? basePath = null) { - ArgumentGuard.NotNullNorEmpty(documentName, nameof(documentName)); + ArgumentGuard.NotNullNorEmpty(documentName); string cacheKey = $"{documentName}#{host}#{basePath}"; diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs index 62a6224c4c..4de3beffe8 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs @@ -13,8 +13,8 @@ internal sealed class EndpointOrderingFilter : IDocumentFilter public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - ArgumentGuard.NotNull(swaggerDoc, nameof(swaggerDoc)); - ArgumentGuard.NotNull(context, nameof(context)); + ArgumentGuard.NotNull(swaggerDoc); + ArgumentGuard.NotNull(context); List> orderedEndpoints = swaggerDoc.Paths.OrderBy(GetPrimaryResourcePublicName) .ThenBy(GetRelationshipName).ThenBy(IsSecondaryEndpoint).ToList(); diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiDataContractResolver.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiDataContractResolver.cs index 2fc7257478..4b6ae76cef 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiDataContractResolver.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiDataContractResolver.cs @@ -16,8 +16,8 @@ internal sealed class JsonApiDataContractResolver : ISerializerDataContractResol public JsonApiDataContractResolver(IResourceGraph resourceGraph, IJsonApiOptions options) { - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(options); _resourceGraph = resourceGraph; _dataContractResolver = new JsonSerializerDataContractResolver(options.SerializerOptions); @@ -25,7 +25,7 @@ public JsonApiDataContractResolver(IResourceGraph resourceGraph, IJsonApiOptions public DataContract GetDataContractForType(Type type) { - ArgumentGuard.NotNull(type, nameof(type)); + ArgumentGuard.NotNull(type); if (type == typeof(IIdentifiable)) { diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs index e0ad6da30c..b4abe196ca 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs @@ -34,9 +34,9 @@ internal sealed class JsonApiSchemaGenerator : ISchemaGenerator public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options) { - ArgumentGuard.NotNull(defaultSchemaGenerator, nameof(defaultSchemaGenerator)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(options, nameof(options)); + ArgumentGuard.NotNull(defaultSchemaGenerator); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(options); _defaultSchemaGenerator = defaultSchemaGenerator; _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(_schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); @@ -46,8 +46,8 @@ public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceG public OpenApiSchema GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null, ApiParameterRouteInfo? routeInfo = null) { - ArgumentGuard.NotNull(modelType, nameof(modelType)); - ArgumentGuard.NotNull(schemaRepository, nameof(schemaRepository)); + ArgumentGuard.NotNull(modelType); + ArgumentGuard.NotNull(schemaRepository); _schemaRepositoryAccessor.Current = schemaRepository; diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs index d9bedc343c..4e09769e87 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs @@ -17,7 +17,7 @@ internal sealed class NullableReferenceSchemaGenerator public NullableReferenceSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor, JsonNamingPolicy? namingPolicy) { - ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); + ArgumentGuard.NotNull(schemaRepositoryAccessor); _schemaRepositoryAccessor = schemaRepositoryAccessor; @@ -26,7 +26,7 @@ public NullableReferenceSchemaGenerator(ISchemaRepositoryAccessor schemaReposito public OpenApiSchema GenerateSchema(OpenApiSchema referenceSchema) { - ArgumentGuard.NotNull(referenceSchema, nameof(referenceSchema)); + ArgumentGuard.NotNull(referenceSchema); return new OpenApiSchema { diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index ef8c705424..48e65d721c 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -30,10 +30,10 @@ internal sealed class ResourceFieldObjectSchemaBuilder public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, JsonNamingPolicy? namingPolicy) { - ArgumentGuard.NotNull(resourceTypeInfo, nameof(resourceTypeInfo)); - ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); - ArgumentGuard.NotNull(defaultSchemaGenerator, nameof(defaultSchemaGenerator)); - ArgumentGuard.NotNull(resourceTypeSchemaGenerator, nameof(resourceTypeSchemaGenerator)); + ArgumentGuard.NotNull(resourceTypeInfo); + ArgumentGuard.NotNull(schemaRepositoryAccessor); + ArgumentGuard.NotNull(defaultSchemaGenerator); + ArgumentGuard.NotNull(resourceTypeSchemaGenerator); _resourceTypeInfo = resourceTypeInfo; _schemaRepositoryAccessor = schemaRepositoryAccessor; diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 616b98ef63..090fcea2dd 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -17,10 +17,10 @@ internal sealed class ResourceObjectSchemaGenerator public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options, ISchemaRepositoryAccessor schemaRepositoryAccessor) { - ArgumentGuard.NotNull(defaultSchemaGenerator, nameof(defaultSchemaGenerator)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - ArgumentGuard.NotNull(options, nameof(options)); - ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); + ArgumentGuard.NotNull(defaultSchemaGenerator); + ArgumentGuard.NotNull(resourceGraph); + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(schemaRepositoryAccessor); _defaultSchemaGenerator = defaultSchemaGenerator; _resourceGraph = resourceGraph; @@ -36,7 +36,7 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe public OpenApiSchema GenerateSchema(Type resourceObjectType) { - ArgumentGuard.NotNull(resourceObjectType, nameof(resourceObjectType)); + ArgumentGuard.NotNull(resourceObjectType); (OpenApiSchema fullSchemaForResourceObject, OpenApiSchema referenceSchemaForResourceObject) = EnsureSchemasExist(resourceObjectType); diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeInfo.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeInfo.cs index 43acb09a03..ea96d89a79 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeInfo.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeInfo.cs @@ -15,8 +15,8 @@ private ResourceTypeInfo(Type resourceObjectOpenType, ResourceType resourceType) public static ResourceTypeInfo Create(Type resourceObjectType, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(resourceObjectType, nameof(resourceObjectType)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(resourceObjectType); + ArgumentGuard.NotNull(resourceGraph); Type resourceObjectOpenType = resourceObjectType.GetGenericTypeDefinition(); Type resourceClrType = resourceObjectType.GenericTypeArguments[0]; diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs index 9113f3b359..592a8ed860 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs @@ -16,8 +16,8 @@ internal sealed class ResourceTypeSchemaGenerator public ResourceTypeSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor, IResourceGraph resourceGraph, JsonNamingPolicy? namingPolicy) { - ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); - ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); + ArgumentGuard.NotNull(schemaRepositoryAccessor); + ArgumentGuard.NotNull(resourceGraph); _schemaRepositoryAccessor = schemaRepositoryAccessor; _resourceGraph = resourceGraph; @@ -26,7 +26,7 @@ public ResourceTypeSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAcc public OpenApiSchema Get(Type resourceClrType) { - ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType)); + ArgumentGuard.NotNull(resourceClrType); if (_resourceClrTypeSchemaCache.TryGetValue(resourceClrType, out OpenApiSchema? referenceSchema)) { diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/SchemaRepositoryAccessor.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/SchemaRepositoryAccessor.cs index 6c2334fbf3..cc5edb6367 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/SchemaRepositoryAccessor.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/SchemaRepositoryAccessor.cs @@ -19,7 +19,7 @@ public SchemaRepository Current } set { - ArgumentGuard.NotNull(value, nameof(Current)); + ArgumentGuard.NotNull(value); _schemaRepository = value; } diff --git a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs b/test/OpenApiClientTests/LegacyClient/ApiResponse.cs index d009d2acee..e62353d17a 100644 --- a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs +++ b/test/OpenApiClientTests/LegacyClient/ApiResponse.cs @@ -12,7 +12,7 @@ internal static class ApiResponse { // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 - ArgumentGuard.NotNull(operation, nameof(operation)); + ArgumentGuard.NotNull(operation); try { diff --git a/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs b/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs index 254f7f31cd..8a5501354f 100644 --- a/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs +++ b/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs @@ -75,7 +75,7 @@ public FakeHttpMessageHandler(HttpResponseMessage response) public void SetResponse(HttpResponseMessage response) { - ArgumentGuard.NotNull(response, nameof(response)); + ArgumentGuard.NotNull(response); _response = response; } diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs index b6a1ce7f34..16ee8bcdaf 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs +++ b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; // @formatter:wrap_chained_method_calls chop_always // @formatter:keep_existing_linebreaks true @@ -7,7 +8,7 @@ namespace OpenApiTests.LegacyOpenApiIntegration; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class LegacyIntegrationDbContext : DbContext +public sealed class LegacyIntegrationDbContext : TestableDbContext { public DbSet Airplanes => Set(); public DbSet Flights => Set(); diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationStartup.cs b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationStartup.cs index 6dbbba3521..2cf6da28ba 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationStartup.cs +++ b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationStartup.cs @@ -2,13 +2,13 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.LegacyOpenApiIntegration; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class LegacyOpenApiIntegrationStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs index 242291c4f6..eeca328a8b 100644 --- a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs +++ b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs @@ -2,13 +2,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.NamingConventions.CamelCase; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class CamelCaseNamingConventionStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs index ebd532aa65..e7534bf657 100644 --- a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs +++ b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs @@ -1,14 +1,14 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; using OpenApiTests.LegacyOpenApiIntegration; +using TestBuildingBlocks; namespace OpenApiTests.NamingConventions.KebabCase; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class KebabCaseNamingConventionStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs b/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs index eb2d0b52d2..0e645e8db0 100644 --- a/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs +++ b/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.NamingConventions; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NamingConventionsDbContext : DbContext +public sealed class NamingConventionsDbContext : TestableDbContext { public DbSet Supermarkets => Set(); diff --git a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs index 7c934b94e4..e51e9029a2 100644 --- a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs +++ b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs @@ -1,13 +1,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.NamingConventions.PascalCase; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class PascalCaseNamingConventionStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/OpenApiStartup.cs b/test/OpenApiTests/OpenApiStartup.cs index dd2a7d50ab..46978e7f67 100644 --- a/test/OpenApiTests/OpenApiStartup.cs +++ b/test/OpenApiTests/OpenApiStartup.cs @@ -1,14 +1,13 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi; using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; namespace OpenApiTests; public abstract class OpenApiStartup : TestableStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { public override void ConfigureServices(IServiceCollection services) { diff --git a/test/OpenApiTests/OpenApiTestContext.cs b/test/OpenApiTests/OpenApiTestContext.cs index a750929827..f720df7d26 100644 --- a/test/OpenApiTests/OpenApiTestContext.cs +++ b/test/OpenApiTests/OpenApiTestContext.cs @@ -1,7 +1,6 @@ using System.Reflection; using System.Text.Json; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; @@ -10,7 +9,7 @@ namespace OpenApiTests; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public class OpenApiTestContext : IntegrationTestContext where TStartup : class - where TDbContext : DbContext + where TDbContext : TestableDbContext { private readonly Lazy> _lazySwaggerDocument; @@ -52,7 +51,7 @@ private static void AssertHasSwaggerDocumentOutputPath([SysNotNull] string? rela { if (relativePath is null) { - throw new Exception($"Property '{nameof(OpenApiTestContext)}.{nameof(SwaggerDocumentOutputPath)}' must be set."); + throw new Exception($"Property '{nameof(OpenApiTestContext)}.{nameof(SwaggerDocumentOutputPath)}' must be set."); } } From 833312d377eacf137f9eceff87b0fc19c529e79a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 9 Nov 2022 00:53:09 +0100 Subject: [PATCH 32/47] Run cibuild on Ubuntu 20.04 LTS instead of 18.04.4 LTS --- Build.ps1 | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index d8be7da5e6..ed1363f626 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -105,7 +105,7 @@ dotnet build -c Release CheckLastExitCode # https://youtrack.jetbrains.com/issue/RSRP-488628/Breaking-InspectCode-fails-with-Roslyn-Worker-process-exited-unexpectedly-after-update -if ($env:APPVEYOR_BUILD_WORKER_IMAGE -ne 'Ubuntu') { +if ($IsWindows) { RunInspectCode RunCleanupCode } diff --git a/appveyor.yml b/appveyor.yml index 61feec2ab8..79a41d39c2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ image: - - Ubuntu + - Ubuntu2004 - Visual Studio 2022 version: '{build}' From 3d4be056030d1986858165ba06948f8ab3666304 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 11 Nov 2022 11:54:57 +0100 Subject: [PATCH 33/47] Use built-in HashSet<>.UnionWith() instead of our own .AddRange() --- src/JsonApiDotNetCore/CollectionExtensions.cs | 11 ----------- .../Queries/Internal/Parsing/IncludeParser.cs | 2 +- .../Queries/Internal/QueryLayerComposer.cs | 2 +- .../Repositories/EntityFrameworkCoreRepository.cs | 2 +- src/JsonApiDotNetCore/Resources/OperationContainer.cs | 2 +- src/JsonApiDotNetCore/Resources/TargetedFields.cs | 4 ++-- .../Request/Adapters/RelationshipDataAdapter.cs | 2 +- .../IntegrationTests/CustomRoutes/CustomRouteTests.cs | 4 ++-- .../RemoveFromToManyRelationshipTests.cs | 4 ++-- .../Relationships/UpdateToOneRelationshipTests.cs | 2 +- .../Resources/UpdateToOneRelationshipTests.cs | 2 +- 11 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/JsonApiDotNetCore/CollectionExtensions.cs b/src/JsonApiDotNetCore/CollectionExtensions.cs index 133231eb23..6a17fed7b9 100644 --- a/src/JsonApiDotNetCore/CollectionExtensions.cs +++ b/src/JsonApiDotNetCore/CollectionExtensions.cs @@ -79,15 +79,4 @@ public static IEnumerable WhereNotNull(this IEnumerable source) return source.Where(element => element is not null)!; #pragma warning restore AV1250 // Evaluate LINQ query before returning it } - - public static void AddRange(this ICollection source, IEnumerable itemsToAdd) - { - ArgumentGuard.NotNull(source); - ArgumentGuard.NotNull(itemsToAdd); - - foreach (T item in itemsToAdd) - { - source.Add(item); - } - } } diff --git a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs index 3c8be88e46..1250e36312 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/Parsing/IncludeParser.cs @@ -110,7 +110,7 @@ private ICollection LookupRelationshipName(string relationshipN if (relationships.Any()) { - relationshipsFound.AddRange(relationships); + relationshipsFound.UnionWith(relationships); RelationshipAttribute[] relationshipsToInclude = relationships.Where(relationship => !relationship.IsIncludeBlocked()).ToArray(); ICollection affectedChildren = parent.EnsureChildren(relationshipsToInclude); diff --git a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs index 29e0935954..4661a5bdda 100644 --- a/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs @@ -254,7 +254,7 @@ private static IImmutableSet ApplyIncludeElementUpdate IDictionary> updatesInChildren) { ImmutableHashSet.Builder newElementsBuilder = ImmutableHashSet.CreateBuilder(); - newElementsBuilder.AddRange(includeElements); + newElementsBuilder.UnionWith(includeElements); foreach ((IncludeElementExpression existingElement, IImmutableSet updatedChildren) in updatesInChildren) { diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs index 653db6129a..7cdd114301 100644 --- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs @@ -471,7 +471,7 @@ private IEnumerable GetRightValueToStoreForAddToToMany(TResource leftResource, H if (rightResourceIdsStored.Any()) { - rightResourceIdsStored.AddRange(rightResourceIdsToAdd); + rightResourceIdsStored.UnionWith(rightResourceIdsToAdd); return rightResourceIdsStored; } diff --git a/src/JsonApiDotNetCore/Resources/OperationContainer.cs b/src/JsonApiDotNetCore/Resources/OperationContainer.cs index a066943c82..88ea29ecdc 100644 --- a/src/JsonApiDotNetCore/Resources/OperationContainer.cs +++ b/src/JsonApiDotNetCore/Resources/OperationContainer.cs @@ -58,6 +58,6 @@ private void AddSecondaryResources(RelationshipAttribute relationship, HashSet rightResources = CollectionConverter.ExtractResources(rightValue); - secondaryResources.AddRange(rightResources); + secondaryResources.UnionWith(rightResources); } } diff --git a/src/JsonApiDotNetCore/Resources/TargetedFields.cs b/src/JsonApiDotNetCore/Resources/TargetedFields.cs index fe4701c61e..4d40fc240d 100644 --- a/src/JsonApiDotNetCore/Resources/TargetedFields.cs +++ b/src/JsonApiDotNetCore/Resources/TargetedFields.cs @@ -18,8 +18,8 @@ public void CopyFrom(ITargetedFields other) { Clear(); - Attributes.AddRange(other.Attributes); - Relationships.AddRange(other.Relationships); + Attributes.UnionWith(other.Attributes); + Relationships.UnionWith(other.Relationships); } public void Clear() diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs index ac1e25746b..0e90f7df07 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs @@ -111,7 +111,7 @@ private IEnumerable ConvertToManyRelationshipData(SingleOrManyData(IdentifiableComparer.Instance); - resourceSet.AddRange(rightResources); + resourceSet.UnionWith(rightResources); return resourceSet; } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs index 572f2baeed..e89641470d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CustomRouteTests.cs @@ -67,12 +67,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_get_resources_at_custom_action_method() { // Arrange - List town = _fakers.Town.Generate(7); + List towns = _fakers.Town.Generate(7); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync(); - dbContext.Towns.AddRange(town); + dbContext.Towns.AddRange(towns); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index eef972dd1f..ed9e4e7936 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -1142,7 +1142,7 @@ private void RemoveFromSubscribers(WorkItem workItem, ISet rightR { if (!workItem.Subscribers.IsNullOrEmpty()) { - PreloadedSubscribers.AddRange(workItem.Subscribers); + PreloadedSubscribers.UnionWith(workItem.Subscribers); } foreach (long subscriberId in ExtraSubscribersIdsToRemove) @@ -1158,7 +1158,7 @@ private void RemoveFromTags(WorkItem workItem, ISet rightResource { if (!workItem.Tags.IsNullOrEmpty()) { - PreloadedTags.AddRange(workItem.Tags); + PreloadedTags.UnionWith(workItem.Tags); } foreach (int tagId in ExtraTagIdsToRemove) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index a492cc4826..b96fcd6fff 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -66,7 +66,7 @@ public async Task Can_clear_OneToOne_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.Groups.AddRange(existingGroup); + dbContext.Groups.Add(existingGroup); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index 81faaa8297..f96f4a9efa 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -209,7 +209,7 @@ public async Task Can_clear_OneToOne_relationship() await _testContext.RunOnDatabaseAsync(async dbContext => { - dbContext.RgbColors.AddRange(existingColor); + dbContext.RgbColors.Add(existingColor); await dbContext.SaveChangesAsync(); }); From 1fff6cdf021563d70f7de763f50e389c5cdb0ded Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 09:10:15 +0100 Subject: [PATCH 34/47] Update to PostgreSQL v15 --- appveyor.yml | 6 +++--- run-docker-postgres.ps1 | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 79a41d39c2..196e7ff0d6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ image: version: '{build}' -stack: postgresql 13.4 +stack: postgresql 15 environment: PGUSER: postgres @@ -34,7 +34,7 @@ for: only: - image: Visual Studio 2022 services: - - postgresql13 + - postgresql15 # REF: https://github.com/docascode/docfx-seed/blob/master/appveyor.yml before_build: - pwsh: | @@ -101,7 +101,7 @@ build_script: Write-Output "PostgreSQL version:" if ($IsWindows) { - . "${env:ProgramFiles}\PostgreSQL\13\bin\psql" --version + . "${env:ProgramFiles}\PostgreSQL\15\bin\psql" --version } else { psql --version diff --git a/run-docker-postgres.ps1 b/run-docker-postgres.ps1 index 89a325e3b5..153b93a846 100644 --- a/run-docker-postgres.ps1 +++ b/run-docker-postgres.ps1 @@ -9,4 +9,4 @@ docker run --rm --name jsonapi-dotnet-core-testing ` -e POSTGRES_USER=postgres ` -e POSTGRES_PASSWORD=postgres ` -p 5432:5432 ` - postgres:13.4 + postgres:15 From 9039dcd58202bfc1fe9544eeb813dc115d5939db Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 12:17:51 +0100 Subject: [PATCH 35/47] Update for changed nullability annotations in .NET 7 --- .../Data/AppDbContext.cs | 2 +- .../Services/WorkItemService.cs | 4 ++-- .../FilterQueryStringParameterReader.cs | 19 +++++++++++-------- .../IncludeQueryStringParameterReader.cs | 2 +- .../PaginationQueryStringParameterReader.cs | 2 +- .../SortQueryStringParameterReader.cs | 2 +- ...parseFieldSetQueryStringParameterReader.cs | 2 +- .../Serialization/Response/LinkBuilder.cs | 4 ++-- .../MusicTrackReleaseDefinition.cs | 2 +- .../Reading/MoonDefinition.cs | 2 +- 10 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs b/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs index c70fc8655f..ba73b8bf3a 100644 --- a/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs +++ b/src/Examples/DatabasePerTenantExample/Data/AppDbContext.cs @@ -36,7 +36,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) private string GetConnectionString() { string? tenantName = GetTenantName(); - string connectionString = _configuration[$"Data:{tenantName ?? "Default"}Connection"]; + string? connectionString = _configuration[$"Data:{tenantName ?? "Default"}Connection"]; if (connectionString == null) { diff --git a/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs index 6df109e5ba..34a40755cb 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs @@ -11,12 +11,12 @@ namespace NoEntityFrameworkExample.Services; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class WorkItemService : IResourceService { - private readonly string _connectionString; + private readonly string? _connectionString; public WorkItemService(IConfiguration configuration) { string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; - _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); + _connectionString = configuration["Data:DefaultConnection"]?.Replace("###", postgresPassword); } public async Task> GetAsync(CancellationToken cancellationToken) diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs index 60c7f6b75c..dace5b8ca4 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/FilterQueryStringParameterReader.cs @@ -77,18 +77,21 @@ public virtual void Read(string parameterName, StringValues parameterValue) } } - private IEnumerable ExtractParameterValue(string parameterValue) + private IEnumerable ExtractParameterValue(string? parameterValue) { - if (_options.EnableLegacyFilterNotation) + if (parameterValue != null) { - foreach (string condition in LegacyConverter.ExtractConditions(parameterValue)) + if (_options.EnableLegacyFilterNotation) { - yield return condition; + foreach (string condition in LegacyConverter.ExtractConditions(parameterValue)) + { + yield return condition; + } + } + else + { + yield return parameterValue; } - } - else - { - yield return parameterValue; } } diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs index a4db6ebd4a..7db9a9a7d7 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/IncludeQueryStringParameterReader.cs @@ -48,7 +48,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) { try { - _includeExpression = GetInclude(parameterValue); + _includeExpression = GetInclude(parameterValue.ToString()); } catch (QueryParseException exception) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs index 416b48f4de..3e4293c5e0 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/PaginationQueryStringParameterReader.cs @@ -53,7 +53,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) { try { - PaginationQueryStringValueExpression constraint = GetPageConstraint(parameterValue); + PaginationQueryStringValueExpression constraint = GetPageConstraint(parameterValue.ToString()); if (constraint.Elements.Any(element => element.Scope == null)) { diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs index 060d0c9986..5e5842c960 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SortQueryStringParameterReader.cs @@ -62,7 +62,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) try { ResourceFieldChainExpression? scope = GetScope(parameterName); - SortExpression sort = GetSort(parameterValue, scope); + SortExpression sort = GetSort(parameterValue.ToString(), scope); var expressionInScope = new ExpressionInScope(scope, sort); _constraints.Add(expressionInScope); diff --git a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs index fb4f665873..09c3c0ede8 100644 --- a/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs @@ -69,7 +69,7 @@ public virtual void Read(string parameterName, StringValues parameterValue) try { ResourceType targetResourceType = GetSparseFieldType(parameterName); - SparseFieldSetExpression sparseFieldSet = GetSparseFieldSet(parameterValue, targetResourceType); + SparseFieldSetExpression sparseFieldSet = GetSparseFieldSet(parameterValue.ToString(), targetResourceType); _sparseFieldTableBuilder[targetResourceType] = sparseFieldSet; } diff --git a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs index ea0eb197df..fb86eb084c 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs @@ -140,13 +140,13 @@ private void SetPaginationInTopLevelLinks(ResourceType resourceType, TopLevelLin private string? CalculatePageSizeValue(PageSize? topPageSize, ResourceType resourceType) { - string pageSizeParameterValue = HttpContext.Request.Query[PageSizeParameterName]; + string? pageSizeParameterValue = HttpContext.Request.Query[PageSizeParameterName]; PageSize? newTopPageSize = Equals(topPageSize, _options.DefaultPageSize) ? null : topPageSize; return ChangeTopPageSize(pageSizeParameterValue, newTopPageSize, resourceType); } - private string? ChangeTopPageSize(string pageSizeParameterValue, PageSize? topPageSize, ResourceType resourceType) + private string? ChangeTopPageSize(string? pageSizeParameterValue, PageSize? topPageSize, ResourceType resourceType) { IImmutableList elements = ParsePageSizeExpression(pageSizeParameterValue, resourceType); int elementInTopScopeIndex = elements.FindIndex(expression => expression.Scope == null); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs index 41aa048ecb..65ab4a4344 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/QueryStrings/MusicTrackReleaseDefinition.cs @@ -32,7 +32,7 @@ private IQueryable FilterOnRecentlyReleased(IQueryable s { IQueryable tracks = source; - if (bool.Parse(parameterValue)) + if (bool.Parse(parameterValue.ToString())) { tracks = tracks.Where(musicTrack => musicTrack.ReleasedAt < _systemClock.UtcNow && musicTrack.ReleasedAt > _systemClock.UtcNow.AddMonths(-3)); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index bdd75a9aff..2b904434ad 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -51,7 +51,7 @@ public override QueryStringParameterHandlers OnRegisterQueryableHandlersFo private static IQueryable FilterByRadius(IQueryable source, StringValues parameterValue) { - bool isFilterOnLargerThan = bool.Parse(parameterValue); + bool isFilterOnLargerThan = bool.Parse(parameterValue.ToString()); return isFilterOnLargerThan ? source.Where(moon => moon.SolarRadius > 1m) : source.Where(moon => moon.SolarRadius <= 1m); } } From 023cdcca1e1b9a34a44fdd4af2e7ded57af70e77 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 12:32:06 +0100 Subject: [PATCH 36/47] Package updates --- .config/dotnet-tools.json | 4 ++-- Directory.Build.props | 6 +++--- test/TestBuildingBlocks/TestBuildingBlocks.csproj | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d914fc369d..7afec1e6ff 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.2.3", + "version": "2022.2.4", "commands": [ "jb" ] @@ -21,7 +21,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.1.3", + "version": "5.1.11", "commands": [ "reportgenerator" ] diff --git a/Directory.Build.props b/Directory.Build.props index 8e68ea532b..1810b9cd46 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + @@ -33,8 +33,8 @@ - 3.1.2 + 3.2.0 4.18.2 - 17.3.1 + 17.4.0 diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 5600104fda..ce8c54ef3b 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -10,7 +10,7 @@ - + From d1fb4d07b161ebcaf10da06465cb69c0b0aaf3d6 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 15:57:05 +0100 Subject: [PATCH 37/47] Turn off constraints for running tests in parallel --- test/AnnotationTests/AnnotationTests.csproj | 6 ------ test/DiscoveryTests/DiscoveryTests.csproj | 6 ------ test/DiscoveryTests/xunit.runner.json | 4 ---- test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj | 6 ------ test/JsonApiDotNetCoreTests/xunit.runner.json | 5 ----- test/MultiDbContextTests/MultiDbContextTests.csproj | 6 ------ test/MultiDbContextTests/xunit.runner.json | 5 ----- test/UnitTests/UnitTests.csproj | 6 ------ test/UnitTests/xunit.runner.json | 4 ---- 9 files changed, 48 deletions(-) delete mode 100644 test/DiscoveryTests/xunit.runner.json delete mode 100644 test/JsonApiDotNetCoreTests/xunit.runner.json delete mode 100644 test/MultiDbContextTests/xunit.runner.json delete mode 100644 test/UnitTests/xunit.runner.json diff --git a/test/AnnotationTests/AnnotationTests.csproj b/test/AnnotationTests/AnnotationTests.csproj index 51df20d735..7b221a9a42 100644 --- a/test/AnnotationTests/AnnotationTests.csproj +++ b/test/AnnotationTests/AnnotationTests.csproj @@ -4,12 +4,6 @@ latest - - - PreserveNewest - - - diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 2f1048de3f..abbec3ed98 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/DiscoveryTests/xunit.runner.json b/test/DiscoveryTests/xunit.runner.json deleted file mode 100644 index 9db029ba52..0000000000 --- a/test/DiscoveryTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -} diff --git a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj index ddb7550e5e..22d50630ca 100644 --- a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj +++ b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/MultiDbContextTests/xunit.runner.json b/test/MultiDbContextTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/MultiDbContextTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index eb3383fbdf..3166fe27e1 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/UnitTests/xunit.runner.json b/test/UnitTests/xunit.runner.json deleted file mode 100644 index 9db029ba52..0000000000 --- a/test/UnitTests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false -} From bfecdebeaa4c5353edfe7deb3bdf22ffc8a11bca Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 16:37:45 +0100 Subject: [PATCH 38/47] Add throttling on integration tests that are running in parallel --- test/TestBuildingBlocks/IntegrationTest.cs | 19 +++++++++++-- .../IntegrationTestContext.cs | 28 +++++++++++-------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index 4fb2e5cd26..92f49879fc 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -2,14 +2,18 @@ using System.Text; using System.Text.Json; using JsonApiDotNetCore.Middleware; +using Xunit; namespace TestBuildingBlocks; /// -/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints. +/// A base class for tests that conveniently enables to execute HTTP requests against JSON:API endpoints. It throttles tests that are running in parallel +/// to avoid exceeding the maximum active database connections. /// -public abstract class IntegrationTest +public abstract class IntegrationTest : IAsyncLifetime { + private static readonly SemaphoreSlim ThrottleSemaphore = new(64); + protected abstract JsonSerializerOptions SerializerOptions { get; } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteHeadAsync(string requestUrl, @@ -105,4 +109,15 @@ public abstract class IntegrationTest throw new FormatException($"Failed to deserialize response body to JSON:\n{responseText}", exception); } } + + public async Task InitializeAsync() + { + await ThrottleSemaphore.WaitAsync(); + } + + public virtual Task DisposeAsync() + { + _ = ThrottleSemaphore.Release(); + return Task.CompletedTask; + } } diff --git a/test/TestBuildingBlocks/IntegrationTestContext.cs b/test/TestBuildingBlocks/IntegrationTestContext.cs index bccf7d8bf3..7856ba67f9 100644 --- a/test/TestBuildingBlocks/IntegrationTestContext.cs +++ b/test/TestBuildingBlocks/IntegrationTestContext.cs @@ -24,7 +24,7 @@ namespace TestBuildingBlocks; /// The Entity Framework Core database context, which can be defined in the test project or API project. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class IntegrationTestContext : IntegrationTest, IDisposable +public class IntegrationTestContext : IntegrationTest where TStartup : class where TDbContext : TestableDbContext { @@ -103,16 +103,6 @@ private WebApplicationFactory CreateFactory() return factoryWithConfiguredContentRoot; } - public void Dispose() - { - if (_lazyFactory.IsValueCreated) - { - RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync()).Wait(); - - _lazyFactory.Value.Dispose(); - } - } - public void ConfigureLogging(Action loggingConfiguration) { _loggingConfiguration = loggingConfiguration; @@ -136,6 +126,22 @@ public async Task RunOnDatabaseAsync(Func asyncAction) await asyncAction(dbContext); } + public override async Task DisposeAsync() + { + try + { + if (_lazyFactory.IsValueCreated) + { + await RunOnDatabaseAsync(async dbContext => await dbContext.Database.EnsureDeletedAsync()); + await _lazyFactory.Value.DisposeAsync(); + } + } + finally + { + await base.DisposeAsync(); + } + } + private sealed class IntegrationTestWebApplicationFactory : WebApplicationFactory { private Action? _loggingConfiguration; From a349fb957acf6e4bdb97fe2c5915ea20fc63aa84 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 18:01:18 +0100 Subject: [PATCH 39/47] Fixed: temporary database created by test not deleted afterwards --- .../AtomicTransactionConsistencyTests.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs index 97f0e08ff5..46ef0c4784 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs @@ -9,7 +9,8 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.Transactions; -public sealed class AtomicTransactionConsistencyTests : IClassFixture, OperationsDbContext>> +public sealed class AtomicTransactionConsistencyTests + : IClassFixture, OperationsDbContext>>, IAsyncLifetime { private readonly IntegrationTestContext, OperationsDbContext> _testContext; private readonly OperationsFakers _fakers = new(); @@ -27,7 +28,7 @@ public AtomicTransactionConsistencyTests(IntegrationTestContext(); string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; - string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; + string dbConnectionString = $"Host=localhost;Port=5432;Database=JsonApiTest-Extra-{Guid.NewGuid():N};User ID=postgres;Password={postgresPassword}"; services.AddDbContext(options => options.UseNpgsql(dbConnectionString)); }); @@ -158,4 +159,22 @@ public async Task Cannot_use_distributed_transaction() error.Source.ShouldNotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } + + public Task InitializeAsync() + { + return Task.CompletedTask; + } + + Task IAsyncLifetime.DisposeAsync() + { + return DeleteExtraDatabaseAsync(); + } + + private async Task DeleteExtraDatabaseAsync() + { + await using AsyncServiceScope scope = _testContext.Factory.Services.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await dbContext.Database.EnsureDeletedAsync(); + } } From fd16904f636b0c201bbcf8f899dd32cc7b714539 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 18:09:42 +0100 Subject: [PATCH 40/47] Avoid potential database name clashes in tests --- .../Configuration/DependencyContainerRegistrationTests.cs | 2 +- test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs index d0bb39a187..8642461d50 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Configuration/DependencyContainerRegistrationTests.cs @@ -74,7 +74,7 @@ private static IHostBuilder CreateValidatingHostBuilder() IHostBuilder hostBuilder = Host.CreateDefaultBuilder().ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureServices(services => - services.AddDbContext(options => options.UseInMemoryDatabase("db"))); + services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()))); webBuilder.UseStartup>(); diff --git a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs index a90c3851ee..0cb24d8025 100644 --- a/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/ServiceCollectionExtensionsTests.cs @@ -25,7 +25,7 @@ public void RegisterResource_DeviatingDbContextPropertyName_RegistersCorrectly() // Arrange var services = new ServiceCollection(); services.AddLogging(); - services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb")); + services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString())); // Act services.AddJsonApi(); From 7cdf273076b83bd66658724fc7d006fba7c493f8 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 12 Nov 2022 19:32:33 +0100 Subject: [PATCH 41/47] Apply global convention for all integration tests: change DeleteBehavior.ClientSetNull to DeleteBehavior.SetNull This improves speed and reduces the number of errors logged by the PostgreSQL server when running tests, because we no longer need the "truncate table cascade" fallback in DbContextExtensions. --- .../AtomicOperations/OperationsDbContext.cs | 2 ++ .../CompositeKeys/CompositeDbContext.cs | 2 ++ .../EagerLoading/EagerLoadingDbContext.cs | 2 ++ .../ModelState/ModelStateDbContext.cs | 2 ++ .../IntegrationTests/Links/LinksDbContext.cs | 2 ++ .../MultiTenancy/MultiTenancyDbContext.cs | 2 ++ .../QueryStrings/Filtering/FilterDbContext.cs | 2 ++ .../QueryStrings/QueryStringDbContext.cs | 2 ++ .../ReadWrite/ReadWriteDbContext.cs | 2 ++ .../DefaultBehaviorDbContext.cs | 2 ++ .../Serialization/SerializationDbContext.cs | 2 ++ .../ResourceInheritanceReadTests.cs | 4 ++-- .../TablePerType/TablePerTypeDbContext.cs | 2 ++ .../SoftDeletion/SoftDeletionDbContext.cs | 2 ++ .../IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs | 2 ++ test/TestBuildingBlocks/DbContextExtensions.cs | 13 +------------ test/TestBuildingBlocks/TestableDbContext.cs | 12 ++++++++++++ 17 files changed, 43 insertions(+), 14 deletions(-) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs index 26dd815521..6fd7817ba7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(musicTrack => musicTrack.OccursIn) .WithMany(playlist => playlist.Tracks); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs index b745208cae..d4850ad428 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs @@ -39,5 +39,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(car => car.PreviousDealerships) .WithMany(dealership => dealership.SoldCars); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs index 030a1a447b..a31deab9a8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/EagerLoadingDbContext.cs @@ -34,5 +34,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(building => building.SecondaryDoor) .WithOne() .HasForeignKey("SecondaryDoorId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs index 73f3241f28..1bfdb1a28e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateDbContext.cs @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(systemDirectory => systemDirectory.AlsoSelf) .WithOne(); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs index ffd2333fbe..149b29b785 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/LinksDbContext.cs @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(photo => photo.Location) .WithOne(location => location.Photo) .HasForeignKey("LocationId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs index 8e1fcd8350..69a6459303 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenancyDbContext.cs @@ -31,5 +31,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasQueryFilter(webProduct => webProduct.Shop.TenantId == _tenantProvider.TenantId); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 1b939ee9a1..35e7b4e51e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -21,5 +21,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .Property(resource => resource.SomeDateTimeInLocalZone) .HasColumnType("timestamp without time zone"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs index 131bfe19fe..473a7428ba 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -37,5 +37,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(man => man.Wife) .WithOne(woman => woman.Husband) .HasForeignKey(); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs index d25acf8a06..4f3d1080cf 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs @@ -49,5 +49,7 @@ protected override void OnModelCreating(ModelBuilder builder) left => left .HasOne(joinEntity => joinEntity.ToItem) .WithMany()); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs index 6ed4deaeff..5b0b839c63 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs @@ -33,5 +33,7 @@ protected override void OnModelCreating(ModelBuilder builder) .HasOne(order => order.Shipment) .WithOne(shipment => shipment.Order) .HasForeignKey("OrderId"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs index 7f62a63b73..4bfb7aa709 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/SerializationDbContext.cs @@ -22,5 +22,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasMany(scholarship => scholarship.Participants) .WithOne(student => student.Scholarship!); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs index 3a7b60e93d..ebca28dac9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/ResourceInheritanceReadTests.cs @@ -2429,7 +2429,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_e await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2); await dbContext.SaveChangesAsync(); }); @@ -2487,7 +2487,7 @@ public async Task Can_sort_on_derived_attribute_from_resource_definition_using_l await _testContext.RunOnDatabaseAsync(async dbContext => { - await dbContext.ClearTableAsync(); + await dbContext.ClearTableAsync(); dbContext.Wheels.AddRange(chromeWheel1, chromeWheel2, chromeWheel3, carbonWheel1, carbonWheel2); await dbContext.SaveChangesAsync(); }); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs index a1549e6332..7c50ee5573 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/TablePerType/TablePerTypeDbContext.cs @@ -32,5 +32,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity().ToTable("GenericProperties"); builder.Entity().ToTable("StringProperties"); builder.Entity().ToTable("NumberProperties"); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs index 2a19ba74b4..3e98950be0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionDbContext.cs @@ -24,5 +24,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasQueryFilter(department => department.SoftDeletedAt == null); + + base.OnModelCreating(builder); } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs index 301a7b6d7b..3e93768683 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/ZeroKeyDbContext.cs @@ -27,5 +27,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(player => player.ActiveGame) .WithMany(game => game.ActivePlayers); + + base.OnModelCreating(builder); } } diff --git a/test/TestBuildingBlocks/DbContextExtensions.cs b/test/TestBuildingBlocks/DbContextExtensions.cs index b570cbf655..5bb3f81a14 100644 --- a/test/TestBuildingBlocks/DbContextExtensions.cs +++ b/test/TestBuildingBlocks/DbContextExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; -using Npgsql; namespace TestBuildingBlocks; @@ -36,17 +35,7 @@ private static async Task ClearTablesAsync(this DbContext dbContext, params Type } string tableName = entityType.GetTableName()!; - - // PERF: We first try to clear the table, which is fast and usually succeeds, unless foreign key constraints are violated. - // In that case, we recursively delete all related data, which is slow. - try - { - await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\""); - } - catch (PostgresException) - { - await dbContext.Database.ExecuteSqlRawAsync($"truncate table \"{tableName}\" cascade"); - } + await dbContext.Database.ExecuteSqlRawAsync($"delete from \"{tableName}\""); } } } diff --git a/test/TestBuildingBlocks/TestableDbContext.cs b/test/TestBuildingBlocks/TestableDbContext.cs index d40db11c03..18ef090baa 100644 --- a/test/TestBuildingBlocks/TestableDbContext.cs +++ b/test/TestBuildingBlocks/TestableDbContext.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using JsonApiDotNetCore; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Logging; namespace TestBuildingBlocks; @@ -17,4 +18,15 @@ protected override void OnConfiguring(DbContextOptionsBuilder builder) // Writes SQL statements to the Output Window when debugging. builder.LogTo(message => Debug.WriteLine(message), DbLoggerCategory.Database.Name.AsArray(), LogLevel.Information); } + + protected override void OnModelCreating(ModelBuilder builder) + { + foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes().SelectMany(entityType => entityType.GetForeignKeys())) + { + if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull) + { + foreignKey.DeleteBehavior = DeleteBehavior.SetNull; + } + } + } } From c7b86014c71c548264dd65634d4640be1e027d7d Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 14 Nov 2022 10:55:22 +0100 Subject: [PATCH 42/47] Start PostgreSQL service explicitly on Windows --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 196e7ff0d6..81ba53020c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,6 +35,9 @@ for: - image: Visual Studio 2022 services: - postgresql15 + install: + # Temporary workaround for https://help.appveyor.com/discussions/questions/60488-postgresql-version + - net start postgresql-x64-15 # REF: https://github.com/docascode/docfx-seed/blob/master/appveyor.yml before_build: - pwsh: | From 109d098fe0a6e73fe956b11e2b1cc7f49d48c2be Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 16 Nov 2022 10:27:24 +0100 Subject: [PATCH 43/47] Update to latest version of regitlint and switch to full cleanup when more than 5 batches are needed --- .config/dotnet-tools.json | 2 +- Build.ps1 | 2 +- cleanupcode.ps1 | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7afec1e6ff..f8589a73a7 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "regitlint": { - "version": "6.1.1", + "version": "6.2.1", "commands": [ "regitlint" ] diff --git a/Build.ps1 b/Build.ps1 index ed1363f626..9b076bc4d8 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -51,7 +51,7 @@ function RunCleanupCode { 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 + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --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/cleanupcode.ps1 b/cleanupcode.ps1 index bab8b82af1..5740ab5a90 100644 --- a/cleanupcode.ps1 +++ b/cleanupcode.ps1 @@ -28,17 +28,17 @@ if ($revision) { 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 + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --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 + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --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 + dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN VerifySuccessExitCode } From 84d01c3067b95b6601868b6195838a2b21e94983 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 8 Dec 2022 13:15:53 +0100 Subject: [PATCH 44/47] Suppress typo warnings in faker-generated text --- .../Serialization/Response/ResponseModelAdapterTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs index d9459f7ec1..39279181dd 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Serialization/Response/ResponseModelAdapterTests.cs @@ -40,6 +40,7 @@ public void Resources_in_deeply_nested_circular_chain_are_written_in_relationshi // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": { ""type"": ""articles"", @@ -145,6 +146,7 @@ public void Resources_in_deeply_nested_circular_chain_are_written_in_relationshi } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] @@ -177,6 +179,7 @@ public void Resources_in_deeply_nested_circular_chains_are_written_in_relationsh // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": [ { @@ -299,6 +302,7 @@ public void Resources_in_deeply_nested_circular_chains_are_written_in_relationsh } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] @@ -335,6 +339,7 @@ public void Resources_in_overlapping_deeply_nested_circular_chains_are_written_i // Assert string text = JsonSerializer.Serialize(document, new JsonSerializerOptions(options.SerializerWriteOptions)); + // ReSharper disable StringLiteralTypo text.Should().BeJson(@"{ ""data"": { ""type"": ""articles"", @@ -514,6 +519,7 @@ public void Resources_in_overlapping_deeply_nested_circular_chains_are_written_i } ] }"); + // ReSharper restore StringLiteralTypo } [Fact] From 0e745913fb23fe05eed0f28ac4e8e68d77e8dfaf Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 15 Dec 2022 11:20:59 +0100 Subject: [PATCH 45/47] Remove xunit config file and call base OnModelCreating in OpenApiTests DbContext. (#1228) --- test/OpenApiClientTests/OpenApiClientTests.csproj | 6 ------ test/OpenApiClientTests/xunit.runner.json | 5 ----- .../LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs | 2 ++ test/OpenApiTests/OpenApiTests.csproj | 6 ------ test/OpenApiTests/xunit.runner.json | 5 ----- 5 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 test/OpenApiClientTests/xunit.runner.json delete mode 100644 test/OpenApiTests/xunit.runner.json diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 41e6fe07e1..4d7bf979ec 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/OpenApiClientTests/xunit.runner.json b/test/OpenApiClientTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/OpenApiClientTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs index 16ee8bcdaf..d3f9672416 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs +++ b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyIntegrationDbContext.cs @@ -32,5 +32,7 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() .HasOne(flight => flight.BackupPurser) .WithMany(); + + base.OnModelCreating(builder); } } diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj index 50b96c1d36..d6cb1d3002 100644 --- a/test/OpenApiTests/OpenApiTests.csproj +++ b/test/OpenApiTests/OpenApiTests.csproj @@ -3,12 +3,6 @@ $(TargetFrameworkName) - - - PreserveNewest - - - diff --git a/test/OpenApiTests/xunit.runner.json b/test/OpenApiTests/xunit.runner.json deleted file mode 100644 index 8f5f10571b..0000000000 --- a/test/OpenApiTests/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} From d7c8b4b04190cec2b900be40bd68e5c0746e272a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:40:56 +0100 Subject: [PATCH 46/47] Post-merge fixes --- .../SchemaProperties/ModelStateValidationDisabledStartup.cs | 4 ++-- .../NullableReferenceTypesDisabledDbContext.cs | 3 ++- .../NullableReferenceTypesEnabledDbContext.cs | 3 ++- test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs index 9895d7d61b..b2906ba460 100644 --- a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs @@ -1,13 +1,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class ModelStateValidationDisabledStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs index 1208a369ef..16bdc07e15 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : DbContext +public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext { public DbSet Chicken => Set(); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs index 01c61aa589..b7011e7d27 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : DbContext +public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext { public DbSet Cow => Set(); diff --git a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs index d4a1bf1d19..03f6531631 100644 --- a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs +++ b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs @@ -1,13 +1,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class SchemaPropertiesStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { From d72e43ee93cfe2f973086dc04925fecc4d5b6c6b Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:37:21 +0100 Subject: [PATCH 47/47] Fixed: do not share NullabilityInfoContext, it is not thread-safe --- .../ResourceFieldAttributeExtensions.cs | 5 ++--- .../SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs | 5 ++--- .../OpenApiClientTests/PropertyInfoAssertionsExtension.cs | 8 ++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs index d4b9c40fea..b1274cbe97 100644 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs @@ -7,8 +7,6 @@ namespace JsonApiDotNetCore.OpenApi; internal static class ResourceFieldAttributeExtensions { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - public static bool IsNullable(this ResourceFieldAttribute source) { bool hasRequiredAttribute = source.Property.HasAttribute(); @@ -24,7 +22,8 @@ public static bool IsNullable(this ResourceFieldAttribute source) return false; } - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(source.Property); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(source.Property); // Reflects the following cases: // Independent of NRT: diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 9d068f3dc9..804fae41fc 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class ResourceFieldObjectSchemaBuilder { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - private static readonly Type[] RelationshipSchemaInResponseOpenTypes = { typeof(ToOneRelationshipInResponse<>), @@ -121,7 +119,8 @@ private bool IsFieldRequired(ResourceFieldAttribute field) bool hasRequiredAttribute = field.Property.HasAttribute(); - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(field.Property); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(field.Property); return field.Property.PropertyType.IsValueType switch { diff --git a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs index c3338a300b..b29c635f8e 100644 --- a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs +++ b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs @@ -6,14 +6,13 @@ namespace OpenApiClientTests; internal static class PropertyInfoAssertionsExtensions { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - [CustomAssertion] public static void BeNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) { PropertyInfo propertyInfo = source.Subject; - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); nullabilityInfo.ReadState.Should().NotBe(NullabilityState.NotNull, because, becauseArgs); } @@ -23,7 +22,8 @@ public static void BeNonNullable(this PropertyInfoAssertions source, string beca { PropertyInfo propertyInfo = source.Subject; - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); nullabilityInfo.ReadState.Should().Be(NullabilityState.NotNull, because, becauseArgs); }