From 73029067bad3d2985fc0859c5f34283782a5f873 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 13:57:15 +0200 Subject: [PATCH 1/5] Fixed: Do not allow the use of 'lid' when client-generated IDs are required --- .../Adapters/AtomicOperationObjectAdapter.cs | 5 ++- .../Adapters/RelationshipDataAdapter.cs | 2 + .../Adapters/ResourceIdentityAdapter.cs | 19 +++++---- .../Adapters/ResourceIdentityRequirements.cs | 16 ++++++++ ...reateResourceWithClientGeneratedIdTests.cs | 41 ++++++++++++++++++- 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicOperationObjectAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicOperationObjectAdapter.cs index d0574f2fa1..795030598c 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicOperationObjectAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/AtomicOperationObjectAdapter.cs @@ -125,7 +125,9 @@ private ResourceIdentityRequirements CreateRefRequirements(RequestAdapterState s return new ResourceIdentityRequirements { EvaluateIdConstraint = resourceType => - ResourceIdentityRequirements.DoEvaluateIdConstraint(resourceType, state.Request.WriteOperation, _options.ClientIdGeneration) + ResourceIdentityRequirements.DoEvaluateIdConstraint(resourceType, state.Request.WriteOperation, _options.ClientIdGeneration), + EvaluateAllowLid = resourceType => + ResourceIdentityRequirements.DoEvaluateAllowLid(resourceType, state.Request.WriteOperation, _options.ClientIdGeneration) }; } @@ -135,6 +137,7 @@ private static ResourceIdentityRequirements CreateDataRequirements(AtomicReferen { ResourceType = refResult.ResourceType, EvaluateIdConstraint = refRequirements.EvaluateIdConstraint, + EvaluateAllowLid = refRequirements.EvaluateAllowLid, IdValue = refResult.Resource.StringId, LidValue = refResult.Resource.LocalId, RelationshipName = refResult.Relationship?.PublicName diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs index 5925524e6b..0e7f292394 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/RelationshipDataAdapter.cs @@ -1,4 +1,5 @@ using System.Collections; +using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCore.Serialization.Objects; @@ -71,6 +72,7 @@ private static SingleOrManyData ToIdentifierData(Singl { ResourceType = relationship.RightType, EvaluateIdConstraint = _ => JsonElementConstraint.Required, + EvaluateAllowLid = _ => state.Request.Kind == EndpointKind.AtomicOperations, RelationshipName = relationship.PublicName }; diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs index be596dae7c..5e25dba0f7 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityAdapter.cs @@ -96,18 +96,20 @@ private static void AssertIsCompatibleResourceType(ResourceType actual, Resource private IIdentifiable CreateResource(ResourceIdentity identity, ResourceIdentityRequirements requirements, ResourceType resourceType, RequestAdapterState state) { - if (state.Request.Kind != EndpointKind.AtomicOperations) + AssertNoIdWithLid(identity, state); + + bool allowLid = requirements.EvaluateAllowLid?.Invoke(resourceType) ?? false; + + if (!allowLid) { AssertHasNoLid(identity, state); } - AssertNoIdWithLid(identity, state); - JsonElementConstraint? idConstraint = requirements.EvaluateIdConstraint?.Invoke(resourceType); if (idConstraint == JsonElementConstraint.Required) { - AssertHasIdOrLid(identity, requirements, state); + AssertHasIdOrLid(identity, requirements, allowLid, state); } else if (idConstraint == JsonElementConstraint.Forbidden) { @@ -128,7 +130,10 @@ private static void AssertHasNoLid(ResourceIdentity identity, RequestAdapterStat if (identity.Lid != null) { using IDisposable _ = state.Position.PushElement("lid"); - throw new ModelConversionException(state.Position, "The 'lid' element is not supported at this endpoint.", null); + + throw state.Request.Kind == EndpointKind.AtomicOperations + ? new ModelConversionException(state.Position, "The 'lid' element cannot be used because a client-generated ID is required.", null) + : new ModelConversionException(state.Position, "The 'lid' element is not supported at this endpoint.", null); } } @@ -140,7 +145,7 @@ private static void AssertNoIdWithLid(ResourceIdentity identity, RequestAdapterS } } - private static void AssertHasIdOrLid(ResourceIdentity identity, ResourceIdentityRequirements requirements, RequestAdapterState state) + private static void AssertHasIdOrLid(ResourceIdentity identity, ResourceIdentityRequirements requirements, bool allowLid, RequestAdapterState state) { string? message = null; @@ -154,7 +159,7 @@ private static void AssertHasIdOrLid(ResourceIdentity identity, ResourceIdentity } else if (identity.Id == null && identity.Lid == null) { - message = state.Request.Kind == EndpointKind.AtomicOperations ? "The 'id' or 'lid' element is required." : "The 'id' element is required."; + message = allowLid ? "The 'id' or 'lid' element is required." : "The 'id' element is required."; } if (message != null) diff --git a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs index 0d26b807d6..0168d2d5ea 100644 --- a/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs +++ b/src/JsonApiDotNetCore/Serialization/Request/Adapters/ResourceIdentityRequirements.cs @@ -21,6 +21,11 @@ public sealed class ResourceIdentityRequirements /// public Func? EvaluateIdConstraint { get; init; } + /// + /// When not null, provides a callback to indicate whether the "lid" element can be used instead of the "id" element. Defaults to false. + /// + public Func? EvaluateAllowLid { get; init; } + /// /// When not null, indicates what the value of the "id" element must be. /// @@ -50,4 +55,15 @@ public sealed class ResourceIdentityRequirements } : JsonElementConstraint.Required; } + + internal static bool DoEvaluateAllowLid(ResourceType resourceType, WriteOperationKind? writeOperation, ClientIdGenerationMode globalClientIdGeneration) + { + if (writeOperation == null) + { + return false; + } + + ClientIdGenerationMode clientIdGeneration = resourceType.ClientIdGeneration ?? globalClientIdGeneration; + return !(writeOperation == WriteOperationKind.CreateResource && clientIdGeneration == ClientIdGenerationMode.Required); + } } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs index 62013322e5..e69cfb7d1a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs @@ -175,7 +175,7 @@ public async Task Cannot_create_resource_for_missing_client_generated_ID() ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); - error.Title.Should().Be("Failed to deserialize request body: The 'id' or 'lid' element is required."); + error.Title.Should().Be("Failed to deserialize request body: The 'id' element is required."); error.Detail.Should().BeNull(); error.Source.ShouldNotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]/data"); @@ -281,6 +281,45 @@ public async Task Cannot_create_resource_for_incompatible_ID() error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); } + [Fact] + public async Task Cannot_create_resource_with_local_ID() + { + // Arrange + var requestBody = new + { + atomic__operations = new[] + { + new + { + op = "add", + data = new + { + type = "textLanguages", + lid = "new-server-id" + } + } + } + }; + + 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: The 'lid' element cannot be used because a client-generated ID is required."); + error.Detail.Should().BeNull(); + error.Source.ShouldNotBeNull(); + error.Source.Pointer.Should().Be("/atomic:operations[0]/data/lid"); + error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + } + [Fact] public async Task Cannot_create_resource_for_ID_and_local_ID() { From b81f78077ba2a45257e32df8a92527de2a814abc Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 14:22:55 +0200 Subject: [PATCH 2/5] Fixed: send absolute/relative URL in location header, depending on options.UseRelativeLinks --- .../Controllers/BaseJsonApiController.cs | 13 ++++++++++++- .../Links/AbsoluteLinksWithNamespaceTests.cs | 2 ++ .../Links/AbsoluteLinksWithoutNamespaceTests.cs | 2 ++ .../Links/RelativeLinksWithNamespaceTests.cs | 2 ++ .../Links/RelativeLinksWithoutNamespaceTests.cs | 2 ++ .../ReadWrite/Creating/CreateResourceTests.cs | 4 ++-- 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index fb3cd2bd2d..9cbc30dead 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -3,6 +3,8 @@ using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -207,7 +209,7 @@ public virtual async Task PostAsync([FromBody] TResource resource TResource? newResource = await _create.CreateAsync(resource, cancellationToken); string resourceId = (newResource ?? resource).StringId!; - string locationUrl = HttpContext.Request.Path.Add($"/{resourceId}"); + string locationUrl = GetLocationUrl(resourceId); if (newResource == null) { @@ -218,6 +220,15 @@ public virtual async Task PostAsync([FromBody] TResource resource return Created(locationUrl, newResource); } + private string GetLocationUrl(string resourceId) + { + PathString locationPath = HttpContext.Request.Path.Add($"/{resourceId}"); + + return _options.UseRelativeLinks + ? UriHelper.BuildRelative(HttpContext.Request.PathBase, locationPath) + : UriHelper.BuildAbsolute(HttpContext.Request.Scheme, HttpContext.Request.Host, HttpContext.Request.PathBase, locationPath); + } + /// /// Adds resources to a to-many relationship. Example: value.Links.Related.Should().Be($"{photoLink}/album"); }); }); + + httpResponse.Headers.Location.Should().Be(albumLink); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs index 6ba9636128..6efcf034fa 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/AbsoluteLinksWithoutNamespaceTests.cs @@ -394,6 +394,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.Should().Be($"{photoLink}/album"); }); }); + + httpResponse.Headers.Location.Should().Be(albumLink); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs index 604f9c3e90..fbb642844f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithNamespaceTests.cs @@ -394,6 +394,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.Should().Be($"{photoLink}/album"); }); }); + + httpResponse.Headers.Location.Should().Be(albumLink); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs index 6ce1effd7c..f767b0cca6 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/RelativeLinksWithoutNamespaceTests.cs @@ -394,6 +394,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.Should().Be($"{photoLink}/album"); }); }); + + httpResponse.Headers.Location.Should().Be(albumLink); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index 917f10be85..39af545d0c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -58,7 +58,7 @@ public async Task Sets_location_header_for_created_resource() httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); string newWorkItemId = responseDocument.Data.SingleValue.ShouldNotBeNull().Id.ShouldNotBeNull(); - httpResponse.Headers.Location.Should().Be($"/workItems/{newWorkItemId}"); + httpResponse.Headers.Location.Should().Be($"http://localhost/workItems/{newWorkItemId}"); responseDocument.Links.ShouldNotBeNull(); responseDocument.Links.Self.Should().Be("http://localhost/workItems/"); @@ -66,7 +66,7 @@ public async Task Sets_location_header_for_created_resource() responseDocument.Data.SingleValue.ShouldNotBeNull(); responseDocument.Data.SingleValue.Links.ShouldNotBeNull(); - responseDocument.Data.SingleValue.Links.Self.Should().Be($"http://localhost{httpResponse.Headers.Location}"); + responseDocument.Data.SingleValue.Links.Self.Should().Be($"{httpResponse.Headers.Location}"); } [Fact] From 3897ebd5c1308642ebc289bf75330ce08b1b7e7d Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 16:26:35 +0200 Subject: [PATCH 3/5] Add [DisallowNull] on TId in pipeline parameters --- .../Repositories/DapperRepository.cs | 7 ++-- .../Services/InMemoryResourceService.cs | 13 +++--- .../Processors/AddToRelationshipProcessor.cs | 2 +- .../Processors/DeleteProcessor.cs | 2 +- .../RemoveFromRelationshipProcessor.cs | 2 +- .../Processors/SetRelationshipProcessor.cs | 2 +- .../Processors/UpdateProcessor.cs | 2 +- .../Controllers/BaseJsonApiController.cs | 21 +++++----- .../Controllers/JsonApiController.cs | 21 ++++++---- .../Queries/IQueryLayerComposer.cs | 11 ++--- .../Queries/QueryLayerComposer.cs | 24 ++++++----- .../EntityFrameworkCoreRepository.cs | 6 +-- .../IResourceRepositoryAccessor.cs | 7 ++-- .../Repositories/IResourceWriteRepository.cs | 8 ++-- .../ResourceRepositoryAccessor.cs | 7 ++-- .../Services/IAddToRelationshipService.cs | 4 +- .../Services/IDeleteService.cs | 3 +- .../Services/IGetByIdService.cs | 3 +- .../Services/IGetRelationshipService.cs | 3 +- .../Services/IGetSecondaryService.cs | 3 +- .../IRemoveFromRelationshipService.cs | 4 +- .../Services/ISetRelationshipService.cs | 3 +- .../Services/IUpdateService.cs | 3 +- .../Services/JsonApiResourceService.cs | 42 +++++++++++-------- .../MultiTenantResourceService.cs | 9 ++-- .../SoftDeletionAwareResourceService.cs | 11 ++--- 26 files changed, 128 insertions(+), 95 deletions(-) diff --git a/src/Examples/DapperExample/Repositories/DapperRepository.cs b/src/Examples/DapperExample/Repositories/DapperRepository.cs index bbbeda2ea3..0c54b34353 100644 --- a/src/Examples/DapperExample/Repositories/DapperRepository.cs +++ b/src/Examples/DapperExample/Repositories/DapperRepository.cs @@ -1,4 +1,5 @@ using System.Data.Common; +using System.Diagnostics.CodeAnalysis; using Dapper; using DapperExample.AtomicOperations; using DapperExample.TranslationToSql; @@ -177,7 +178,7 @@ public async Task CountAsync(FilterExpression? filter, CancellationToken ca } /// - public Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken) + public Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) { ArgumentGuard.NotNull(resourceClrType); @@ -355,7 +356,7 @@ await ExecuteInTransactionAsync(async transaction => } /// - public async Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken) + public async Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken) { TResource placeholderResource = resourceFromDatabase ?? _resourceFactory.CreateInstance(); placeholderResource.Id = id; @@ -451,7 +452,7 @@ await ExecuteInTransactionAsync(async transaction => } /// - public async Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, + public async Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) { ArgumentGuard.NotNull(rightResourceIds); diff --git a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs index e9b37560fc..ee9d2196e9 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; using JsonApiDotNetCore.Queries; @@ -114,7 +115,7 @@ private bool SetPrimaryTotalCountIsZero() } /// - public Task GetAsync(TId id, CancellationToken cancellationToken) + public Task GetAsync([DisallowNull] TId id, CancellationToken cancellationToken) { QueryLayer queryLayer = _queryLayerComposer.ComposeForGetById(id, _resourceType, TopFieldSelection.PreserveExisting); @@ -124,14 +125,14 @@ public Task GetAsync(TId id, CancellationToken cancellationToken) if (resource == null) { - throw new ResourceNotFoundException(id!.ToString()!, _resourceType.PublicName); + throw new ResourceNotFoundException(id.ToString()!, _resourceType.PublicName); } return Task.FromResult(resource); } /// - public Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public Task GetSecondaryAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { RelationshipAttribute? relationship = _resourceType.FindRelationshipByPublicName(relationshipName); @@ -151,7 +152,7 @@ public Task GetAsync(TId id, CancellationToken cancellationToken) if (primaryResource == null) { - throw new ResourceNotFoundException(id!.ToString()!, _resourceType.PublicName); + throw new ResourceNotFoundException(id.ToString()!, _resourceType.PublicName); } object? rightValue = relationship.GetValue(primaryResource); @@ -164,7 +165,7 @@ public Task GetAsync(TId id, CancellationToken cancellationToken) return Task.FromResult(rightValue); } - private void SetNonPrimaryTotalCount(TId id, RelationshipAttribute relationship) + private void SetNonPrimaryTotalCount([DisallowNull] TId id, RelationshipAttribute relationship) { if (_options.IncludeTotalResourceCount && relationship is HasManyAttribute hasManyRelationship) { @@ -188,7 +189,7 @@ private void SetNonPrimaryTotalCount(TId id, RelationshipAttribute relationship) } /// - public Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public Task GetRelationshipAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { return GetSecondaryAsync(id, relationshipName, cancellationToken); } diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs index 5e63a2d276..fa835910ef 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/AddToRelationshipProcessor.cs @@ -26,7 +26,7 @@ public AddToRelationshipProcessor(IAddToRelationshipService serv var leftId = (TId)operation.Resource.GetTypedId(); ISet rightResourceIds = operation.GetSecondaryResources(); - await _service.AddToToManyRelationshipAsync(leftId, operation.Request.Relationship!.PublicName, rightResourceIds, cancellationToken); + await _service.AddToToManyRelationshipAsync(leftId!, operation.Request.Relationship!.PublicName, rightResourceIds, cancellationToken); return null; } diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs index 6652d4ddec..d758e065bc 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/DeleteProcessor.cs @@ -24,7 +24,7 @@ public DeleteProcessor(IDeleteService service) ArgumentGuard.NotNull(operation); var id = (TId)operation.Resource.GetTypedId(); - await _service.DeleteAsync(id, cancellationToken); + await _service.DeleteAsync(id!, cancellationToken); return null; } diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs index 5fae04f710..6222a01fd8 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/RemoveFromRelationshipProcessor.cs @@ -26,7 +26,7 @@ public RemoveFromRelationshipProcessor(IRemoveFromRelationshipService rightResourceIds = operation.GetSecondaryResources(); - await _service.RemoveFromToManyRelationshipAsync(leftId, operation.Request.Relationship!.PublicName, rightResourceIds, cancellationToken); + await _service.RemoveFromToManyRelationshipAsync(leftId!, operation.Request.Relationship!.PublicName, rightResourceIds, cancellationToken); return null; } diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs index 2de4229aa7..3eeaa77fb3 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs @@ -28,7 +28,7 @@ public SetRelationshipProcessor(ISetRelationshipService service) var leftId = (TId)operation.Resource.GetTypedId(); object? rightValue = GetRelationshipRightValue(operation); - await _service.SetRelationshipAsync(leftId, operation.Request.Relationship!.PublicName, rightValue, cancellationToken); + await _service.SetRelationshipAsync(leftId!, operation.Request.Relationship!.PublicName, rightValue, cancellationToken); return null; } diff --git a/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs b/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs index b221951fd2..32fe2a1eb5 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/Processors/UpdateProcessor.cs @@ -24,7 +24,7 @@ public UpdateProcessor(IUpdateService service) ArgumentGuard.NotNull(operation); var resource = (TResource)operation.Resource; - TResource? updated = await _service.UpdateAsync(resource.Id, resource, cancellationToken); + TResource? updated = await _service.UpdateAsync(resource.Id!, resource, cancellationToken); return updated == null ? null : operation.WithResource(updated); } diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index 9cbc30dead..bef962a971 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; using JsonApiDotNetCore.Middleware; @@ -108,7 +109,7 @@ public virtual async Task GetAsync(CancellationToken cancellation /// GET /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task GetAsync(TId id, CancellationToken cancellationToken) + public virtual async Task GetAsync([DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -133,7 +134,7 @@ public virtual async Task GetAsync(TId id, CancellationToken canc /// GET /articles/1/revisions HTTP/1.1 /// ]]> /// - public virtual async Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetSecondaryAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -162,7 +163,7 @@ public virtual async Task GetSecondaryAsync(TId id, string relati /// GET /articles/1/relationships/revisions HTTP/1.1 /// ]]> /// - public virtual async Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetRelationshipAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -246,8 +247,8 @@ private string GetLocationUrl(string resourceId) /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task PostRelationshipAsync(TId id, string relationshipName, [FromBody] ISet rightResourceIds, - CancellationToken cancellationToken) + public virtual async Task PostRelationshipAsync([DisallowNull] TId id, string relationshipName, + [FromBody] ISet rightResourceIds, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -275,7 +276,7 @@ public virtual async Task PostRelationshipAsync(TId id, string re /// PATCH /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task PatchAsync(TId id, [FromBody] TResource resource, CancellationToken cancellationToken) + public virtual async Task PatchAsync([DisallowNull] TId id, [FromBody] TResource resource, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -321,7 +322,7 @@ public virtual async Task PatchAsync(TId id, [FromBody] TResource /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task PatchRelationshipAsync(TId id, string relationshipName, [FromBody] object? rightValue, + public virtual async Task PatchRelationshipAsync([DisallowNull] TId id, string relationshipName, [FromBody] object? rightValue, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new @@ -348,7 +349,7 @@ public virtual async Task PatchRelationshipAsync(TId id, string r /// DELETE /articles/1 HTTP/1.1 /// ]]> /// - public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToken) + public virtual async Task DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -382,8 +383,8 @@ public virtual async Task DeleteAsync(TId id, CancellationToken c /// /// Propagates notification that request handling should be canceled. /// - public virtual async Task DeleteRelationshipAsync(TId id, string relationshipName, [FromBody] ISet rightResourceIds, - CancellationToken cancellationToken) + public virtual async Task DeleteRelationshipAsync([DisallowNull] TId id, string relationshipName, + [FromBody] ISet rightResourceIds, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs index 127b37ca84..e5be3cbc71 100644 --- a/src/JsonApiDotNetCore/Controllers/JsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/JsonApiController.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Services; @@ -50,7 +51,7 @@ public override async Task GetAsync(CancellationToken cancellatio /// [HttpGet("{id}")] [HttpHead("{id}")] - public override async Task GetAsync([Required] TId id, CancellationToken cancellationToken) + public override async Task GetAsync([Required] [DisallowNull] TId id, CancellationToken cancellationToken) { return await base.GetAsync(id, cancellationToken); } @@ -58,7 +59,8 @@ public override async Task GetAsync([Required] TId id, Cancellati /// [HttpGet("{id}/{relationshipName}")] [HttpHead("{id}/{relationshipName}")] - public override async Task GetSecondaryAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) + public override async Task GetSecondaryAsync([Required] [DisallowNull] TId id, [Required] string relationshipName, + CancellationToken cancellationToken) { return await base.GetSecondaryAsync(id, relationshipName, cancellationToken); } @@ -66,7 +68,8 @@ public override async Task GetSecondaryAsync([Required] TId id, [ /// [HttpGet("{id}/relationships/{relationshipName}")] [HttpHead("{id}/relationships/{relationshipName}")] - public override async Task GetRelationshipAsync([Required] TId id, [Required] string relationshipName, CancellationToken cancellationToken) + public override async Task GetRelationshipAsync([Required] [DisallowNull] TId id, [Required] string relationshipName, + CancellationToken cancellationToken) { return await base.GetRelationshipAsync(id, relationshipName, cancellationToken); } @@ -80,7 +83,7 @@ public override async Task PostAsync([Required] TResource resourc /// [HttpPost("{id}/relationships/{relationshipName}")] - public override async Task PostRelationshipAsync([Required] TId id, [Required] string relationshipName, + public override async Task PostRelationshipAsync([Required] [DisallowNull] TId id, [Required] string relationshipName, [Required] ISet rightResourceIds, CancellationToken cancellationToken) { return await base.PostRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken); @@ -88,7 +91,7 @@ public override async Task PostRelationshipAsync([Required] TId i /// [HttpPatch("{id}")] - public override async Task PatchAsync([Required] TId id, [Required] TResource resource, CancellationToken cancellationToken) + public override async Task PatchAsync([Required] [DisallowNull] TId id, [Required] TResource resource, CancellationToken cancellationToken) { return await base.PatchAsync(id, resource, cancellationToken); } @@ -96,22 +99,22 @@ public override async Task PatchAsync([Required] TId id, [Require /// [HttpPatch("{id}/relationships/{relationshipName}")] // Parameter `[Required] object? rightValue` makes Swashbuckle generate the OpenAPI request body as required. We don't actually validate ModelState, so it doesn't hurt. - public override async Task PatchRelationshipAsync([Required] TId id, [Required] string relationshipName, [Required] object? rightValue, - CancellationToken cancellationToken) + public override async Task PatchRelationshipAsync([Required] [DisallowNull] TId id, [Required] string relationshipName, + [Required] object? rightValue, CancellationToken cancellationToken) { return await base.PatchRelationshipAsync(id, relationshipName, rightValue, cancellationToken); } /// [HttpDelete("{id}")] - public override async Task DeleteAsync([Required] TId id, CancellationToken cancellationToken) + public override async Task DeleteAsync([Required] [DisallowNull] TId id, CancellationToken cancellationToken) { return await base.DeleteAsync(id, cancellationToken); } /// [HttpDelete("{id}/relationships/{relationshipName}")] - public override async Task DeleteRelationshipAsync([Required] TId id, [Required] string relationshipName, + public override async Task DeleteRelationshipAsync([Required] [DisallowNull] TId id, [Required] string relationshipName, [Required] ISet rightResourceIds, CancellationToken cancellationToken) { return await base.DeleteRelationshipAsync(id, relationshipName, rightResourceIds, cancellationToken); diff --git a/src/JsonApiDotNetCore/Queries/IQueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/IQueryLayerComposer.cs index a9d99c3b13..8b14590e83 100644 --- a/src/JsonApiDotNetCore/Queries/IQueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/IQueryLayerComposer.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Resources; @@ -18,7 +19,7 @@ public interface IQueryLayerComposer /// /// Builds a filter from constraints, used to determine total resource count on a secondary collection endpoint. /// - FilterExpression? GetSecondaryFilterFromConstraints(TId primaryId, HasManyAttribute hasManyRelationship); + FilterExpression? GetSecondaryFilterFromConstraints([DisallowNull] TId primaryId, HasManyAttribute hasManyRelationship); /// /// Collects constraints and builds a out of them, used to retrieve the actual resources. @@ -28,7 +29,7 @@ public interface IQueryLayerComposer /// /// Collects constraints and builds a out of them, used to retrieve one resource. /// - QueryLayer ComposeForGetById(TId id, ResourceType primaryResourceType, TopFieldSelection fieldSelection); + QueryLayer ComposeForGetById([DisallowNull] TId id, ResourceType primaryResourceType, TopFieldSelection fieldSelection); /// /// Collects constraints and builds the secondary layer for a relationship endpoint. @@ -38,14 +39,14 @@ public interface IQueryLayerComposer /// /// Wraps a layer for a secondary endpoint into a primary layer, rewriting top-level includes. /// - QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, ResourceType primaryResourceType, TId primaryId, + QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, ResourceType primaryResourceType, [DisallowNull] TId primaryId, RelationshipAttribute relationship); /// /// Builds a query that retrieves the primary resource, including all of its attributes and all targeted relationships, during a create/update/delete /// request. /// - QueryLayer ComposeForUpdate(TId id, ResourceType primaryResourceType); + QueryLayer ComposeForUpdate([DisallowNull] TId id, ResourceType primaryResourceType); /// /// Builds a query for each targeted relationship with a filter to match on its right resource IDs. @@ -60,5 +61,5 @@ QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, Resourc /// /// Builds a query for a to-many relationship with a filter to match on its left and right resource IDs. /// - QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, TId leftId, ICollection rightResourceIds); + QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, [DisallowNull] TId leftId, ICollection rightResourceIds); } diff --git a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs index 8e0c97e6c0..1c4ca77119 100644 --- a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Diagnostics; @@ -63,7 +64,7 @@ public QueryLayerComposer(IEnumerable constraintProvid } /// - public FilterExpression? GetSecondaryFilterFromConstraints(TId primaryId, HasManyAttribute hasManyRelationship) + public FilterExpression? GetSecondaryFilterFromConstraints([DisallowNull] TId primaryId, HasManyAttribute hasManyRelationship) { ArgumentGuard.NotNull(hasManyRelationship); @@ -102,26 +103,29 @@ public QueryLayerComposer(IEnumerable constraintProvid return LogicalExpression.Compose(LogicalOperator.And, inverseFilter, primaryFilter, secondaryFilter); } - private static FilterExpression GetInverseRelationshipFilter(TId primaryId, HasManyAttribute relationship, RelationshipAttribute inverseRelationship) + private static FilterExpression GetInverseRelationshipFilter([DisallowNull] TId primaryId, HasManyAttribute relationship, + RelationshipAttribute inverseRelationship) { return inverseRelationship is HasManyAttribute hasManyInverseRelationship ? GetInverseHasManyRelationshipFilter(primaryId, relationship, hasManyInverseRelationship) : GetInverseHasOneRelationshipFilter(primaryId, relationship, (HasOneAttribute)inverseRelationship); } - private static FilterExpression GetInverseHasOneRelationshipFilter(TId primaryId, HasManyAttribute relationship, HasOneAttribute inverseRelationship) + private static FilterExpression GetInverseHasOneRelationshipFilter([DisallowNull] TId primaryId, HasManyAttribute relationship, + HasOneAttribute inverseRelationship) { AttrAttribute idAttribute = GetIdAttribute(relationship.LeftType); var idChain = new ResourceFieldChainExpression(ImmutableArray.Create(inverseRelationship, idAttribute)); - return new ComparisonExpression(ComparisonOperator.Equals, idChain, new LiteralConstantExpression(primaryId!)); + return new ComparisonExpression(ComparisonOperator.Equals, idChain, new LiteralConstantExpression(primaryId)); } - private static FilterExpression GetInverseHasManyRelationshipFilter(TId primaryId, HasManyAttribute relationship, HasManyAttribute inverseRelationship) + private static FilterExpression GetInverseHasManyRelationshipFilter([DisallowNull] TId primaryId, HasManyAttribute relationship, + HasManyAttribute inverseRelationship) { AttrAttribute idAttribute = GetIdAttribute(relationship.LeftType); var idChain = new ResourceFieldChainExpression(ImmutableArray.Create(idAttribute)); - var idComparison = new ComparisonExpression(ComparisonOperator.Equals, idChain, new LiteralConstantExpression(primaryId!)); + var idComparison = new ComparisonExpression(ComparisonOperator.Equals, idChain, new LiteralConstantExpression(primaryId)); return new HasExpression(new ResourceFieldChainExpression(inverseRelationship), idComparison); } @@ -263,7 +267,7 @@ private static IImmutableSet ApplyIncludeElementUpdate } /// - public QueryLayer ComposeForGetById(TId id, ResourceType primaryResourceType, TopFieldSelection fieldSelection) + public QueryLayer ComposeForGetById([DisallowNull] TId id, ResourceType primaryResourceType, TopFieldSelection fieldSelection) { ArgumentGuard.NotNull(primaryResourceType); @@ -314,7 +318,7 @@ private FieldSelection GetSelectionForRelationship(ResourceType secondaryResourc } /// - public QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, ResourceType primaryResourceType, TId primaryId, + public QueryLayer WrapLayerForSecondaryEndpoint(QueryLayer secondaryLayer, ResourceType primaryResourceType, [DisallowNull] TId primaryId, RelationshipAttribute relationship) { ArgumentGuard.NotNull(secondaryLayer); @@ -377,7 +381,7 @@ private IncludeExpression RewriteIncludeForSecondaryEndpoint(IncludeExpression? } /// - public QueryLayer ComposeForUpdate(TId id, ResourceType primaryResourceType) + public QueryLayer ComposeForUpdate([DisallowNull] TId id, ResourceType primaryResourceType) { ArgumentGuard.NotNull(primaryResourceType); @@ -440,7 +444,7 @@ public QueryLayer ComposeForGetRelationshipRightIds(RelationshipAttribute relati } /// - public QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, TId leftId, ICollection rightResourceIds) + public QueryLayer ComposeForHasMany(HasManyAttribute hasManyRelationship, [DisallowNull] TId leftId, ICollection rightResourceIds) { ArgumentGuard.NotNull(hasManyRelationship); ArgumentGuard.NotNull(rightResourceIds); diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs index a5f083932b..72eeff5c27 100644 --- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs @@ -179,7 +179,7 @@ protected virtual IQueryable GetAll() } /// - public virtual Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken) + public virtual Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -332,7 +332,7 @@ protected void AssertIsNotClearingRequiredToOneRelationship(RelationshipAttribut } /// - public virtual async Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken) + public virtual async Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -432,7 +432,7 @@ public virtual async Task SetRelationshipAsync(TResource leftResource, object? r } /// - public virtual async Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, + public virtual async Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new diff --git a/src/JsonApiDotNetCore/Repositories/IResourceRepositoryAccessor.cs b/src/JsonApiDotNetCore/Repositories/IResourceRepositoryAccessor.cs index 149fef1cfb..a58542c239 100644 --- a/src/JsonApiDotNetCore/Repositories/IResourceRepositoryAccessor.cs +++ b/src/JsonApiDotNetCore/Repositories/IResourceRepositoryAccessor.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Queries.Expressions; @@ -29,7 +30,7 @@ Task> GetAsync(QueryLayer queryLayer, /// /// Invokes for the specified resource type. /// - Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken) + Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) where TResource : class, IIdentifiable; /// @@ -53,7 +54,7 @@ Task UpdateAsync(TResource resourceFromRequest, TResource resourceFro /// /// Invokes for the specified resource type. /// - Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken) + Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken) where TResource : class, IIdentifiable; /// @@ -65,7 +66,7 @@ Task SetRelationshipAsync(TResource leftResource, object? rightValue, /// /// Invokes for the specified resource type. /// - Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, + Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) where TResource : class, IIdentifiable; diff --git a/src/JsonApiDotNetCore/Repositories/IResourceWriteRepository.cs b/src/JsonApiDotNetCore/Repositories/IResourceWriteRepository.cs index fb0267d18a..49d2c60d73 100644 --- a/src/JsonApiDotNetCore/Repositories/IResourceWriteRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/IResourceWriteRepository.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Resources; @@ -23,7 +24,7 @@ public interface IResourceWriteRepository /// /// This method can be overridden to assign resource-specific required relationships. /// - Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken); + Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken); /// /// Creates a new resource in the underlying data store. @@ -43,7 +44,7 @@ public interface IResourceWriteRepository /// /// Deletes an existing resource from the underlying data store. /// - Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken); + Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken); /// /// Performs a complete replacement of the relationship in the underlying data store. @@ -53,7 +54,8 @@ public interface IResourceWriteRepository /// /// Adds resources to a to-many relationship in the underlying data store. /// - Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, CancellationToken cancellationToken); + Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, + CancellationToken cancellationToken); /// /// Removes resources from a to-many relationship in the underlying data store. diff --git a/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs b/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs index 6370ddb883..6f19f51c1d 100644 --- a/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs +++ b/src/JsonApiDotNetCore/Repositories/ResourceRepositoryAccessor.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; @@ -53,7 +54,7 @@ public async Task CountAsync(ResourceType resourceType, FilterExpression? f } /// - public async Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken) + public async Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) where TResource : class, IIdentifiable { dynamic repository = GetWriteRepository(typeof(TResource)); @@ -85,7 +86,7 @@ public async Task UpdateAsync(TResource resourceFromRequest, TResourc } /// - public async Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken) + public async Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken) where TResource : class, IIdentifiable { dynamic repository = GetWriteRepository(typeof(TResource)); @@ -101,7 +102,7 @@ public async Task SetRelationshipAsync(TResource leftResource, object } /// - public async Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, + public async Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) where TResource : class, IIdentifiable { diff --git a/src/JsonApiDotNetCore/Services/IAddToRelationshipService.cs b/src/JsonApiDotNetCore/Services/IAddToRelationshipService.cs index 58fb122a50..07c9234513 100644 --- a/src/JsonApiDotNetCore/Services/IAddToRelationshipService.cs +++ b/src/JsonApiDotNetCore/Services/IAddToRelationshipService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Resources; @@ -25,5 +26,6 @@ public interface IAddToRelationshipService /// /// Propagates notification that request handling should be canceled. /// - Task AddToToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken); + Task AddToToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, + CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IDeleteService.cs b/src/JsonApiDotNetCore/Services/IDeleteService.cs index 9bdfcd143b..375181e529 100644 --- a/src/JsonApiDotNetCore/Services/IDeleteService.cs +++ b/src/JsonApiDotNetCore/Services/IDeleteService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; // ReSharper disable UnusedTypeParameter @@ -11,5 +12,5 @@ public interface IDeleteService /// /// Handles a JSON:API request to delete an existing resource. /// - Task DeleteAsync(TId id, CancellationToken cancellationToken); + Task DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IGetByIdService.cs b/src/JsonApiDotNetCore/Services/IGetByIdService.cs index 4bf34788eb..fc95e0af1e 100644 --- a/src/JsonApiDotNetCore/Services/IGetByIdService.cs +++ b/src/JsonApiDotNetCore/Services/IGetByIdService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; namespace JsonApiDotNetCore.Services; @@ -9,5 +10,5 @@ public interface IGetByIdService /// /// Handles a JSON:API request to retrieve a single resource for a primary endpoint. /// - Task GetAsync(TId id, CancellationToken cancellationToken); + Task GetAsync([DisallowNull] TId id, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IGetRelationshipService.cs b/src/JsonApiDotNetCore/Services/IGetRelationshipService.cs index afd284a7ce..34b9880bea 100644 --- a/src/JsonApiDotNetCore/Services/IGetRelationshipService.cs +++ b/src/JsonApiDotNetCore/Services/IGetRelationshipService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; // ReSharper disable UnusedTypeParameter @@ -11,5 +12,5 @@ public interface IGetRelationshipService /// /// Handles a JSON:API request to retrieve a single relationship. /// - Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken); + Task GetRelationshipAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IGetSecondaryService.cs b/src/JsonApiDotNetCore/Services/IGetSecondaryService.cs index 9f8c528552..33d47db454 100644 --- a/src/JsonApiDotNetCore/Services/IGetSecondaryService.cs +++ b/src/JsonApiDotNetCore/Services/IGetSecondaryService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; // ReSharper disable UnusedTypeParameter @@ -12,5 +13,5 @@ public interface IGetSecondaryService /// Handles a JSON:API request to retrieve a single resource or a collection of resources for a secondary endpoint, such as /articles/1/author or /// /articles/1/revisions. /// - Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken); + Task GetSecondaryAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IRemoveFromRelationshipService.cs b/src/JsonApiDotNetCore/Services/IRemoveFromRelationshipService.cs index cb572801bb..d3844610c7 100644 --- a/src/JsonApiDotNetCore/Services/IRemoveFromRelationshipService.cs +++ b/src/JsonApiDotNetCore/Services/IRemoveFromRelationshipService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; // ReSharper disable UnusedTypeParameter @@ -23,5 +24,6 @@ public interface IRemoveFromRelationshipService /// /// Propagates notification that request handling should be canceled. /// - Task RemoveFromToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken); + Task RemoveFromToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, + CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/ISetRelationshipService.cs b/src/JsonApiDotNetCore/Services/ISetRelationshipService.cs index 3050394beb..05e8c8c606 100644 --- a/src/JsonApiDotNetCore/Services/ISetRelationshipService.cs +++ b/src/JsonApiDotNetCore/Services/ISetRelationshipService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; // ReSharper disable UnusedTypeParameter @@ -23,5 +24,5 @@ public interface ISetRelationshipService /// /// Propagates notification that request handling should be canceled. /// - Task SetRelationshipAsync(TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken); + Task SetRelationshipAsync([DisallowNull] TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/IUpdateService.cs b/src/JsonApiDotNetCore/Services/IUpdateService.cs index f742a1fc2e..b8349c3909 100644 --- a/src/JsonApiDotNetCore/Services/IUpdateService.cs +++ b/src/JsonApiDotNetCore/Services/IUpdateService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JsonApiDotNetCore.Resources; namespace JsonApiDotNetCore.Services; @@ -10,5 +11,5 @@ public interface IUpdateService /// Handles a JSON:API request to update the attributes and/or relationships of an existing resource. Only the values of sent attributes are replaced. /// And only the values of sent relationships are replaced. /// - Task UpdateAsync(TId id, TResource resource, CancellationToken cancellationToken); + Task UpdateAsync([DisallowNull] TId id, TResource resource, CancellationToken cancellationToken); } diff --git a/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs b/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs index 5984b6215b..478d38ab72 100644 --- a/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs +++ b/src/JsonApiDotNetCore/Services/JsonApiResourceService.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; @@ -85,7 +86,7 @@ public virtual async Task> GetAsync(CancellationT } /// - public virtual async Task GetAsync(TId id, CancellationToken cancellationToken) + public virtual async Task GetAsync([DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -98,7 +99,7 @@ public virtual async Task GetAsync(TId id, CancellationToken cancella } /// - public virtual async Task GetSecondaryAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetSecondaryAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -137,7 +138,7 @@ public virtual async Task GetAsync(TId id, CancellationToken cancella } /// - public virtual async Task GetRelationshipAsync(TId id, string relationshipName, CancellationToken cancellationToken) + public virtual async Task GetRelationshipAsync([DisallowNull] TId id, string relationshipName, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -177,7 +178,8 @@ public virtual async Task GetAsync(TId id, CancellationToken cancella return rightValue; } - private async Task RetrieveResourceCountForNonPrimaryEndpointAsync(TId id, HasManyAttribute relationship, CancellationToken cancellationToken) + private async Task RetrieveResourceCountForNonPrimaryEndpointAsync([DisallowNull] TId id, HasManyAttribute relationship, + CancellationToken cancellationToken) { FilterExpression? secondaryFilter = _queryLayerComposer.GetSecondaryFilterFromConstraints(id, relationship); @@ -205,7 +207,10 @@ private async Task RetrieveResourceCountForNonPrimaryEndpointAsync(TId id, HasMa await AccurizeResourceTypesInHierarchyToAssignInRelationshipsAsync(resourceFromRequest, cancellationToken); Type resourceClrType = resourceFromRequest.GetClrType(); - TResource resourceForDatabase = await _repositoryAccessor.GetForCreateAsync(resourceClrType, resourceFromRequest.Id, cancellationToken); + + TResource resourceForDatabase = + await _repositoryAccessor.GetForCreateAsync(resourceClrType, resourceFromRequest.Id!, cancellationToken); + AccurizeJsonApiRequest(resourceForDatabase); _resourceChangeTracker.SetInitiallyStoredAttributeValues(resourceForDatabase); @@ -223,7 +228,7 @@ private async Task RetrieveResourceCountForNonPrimaryEndpointAsync(TId id, HasMa throw; } - TResource resourceFromDatabase = await GetPrimaryResourceByIdAsync(resourceForDatabase.Id, TopFieldSelection.WithAllAttributes, cancellationToken); + TResource resourceFromDatabase = await GetPrimaryResourceByIdAsync(resourceForDatabase.Id!, TopFieldSelection.WithAllAttributes, cancellationToken); _resourceChangeTracker.SetFinallyStoredAttributeValues(resourceFromDatabase); @@ -235,7 +240,7 @@ protected async Task AssertPrimaryResourceDoesNotExistAsync(TResource resource, { if (!Equals(resource.Id, default(TId))) { - TResource? existingResource = await GetPrimaryResourceByIdOrDefaultAsync(resource.Id, TopFieldSelection.OnlyIdAttribute, cancellationToken); + TResource? existingResource = await GetPrimaryResourceByIdOrDefaultAsync(resource.Id!, TopFieldSelection.OnlyIdAttribute, cancellationToken); if (existingResource != null) { @@ -329,7 +334,7 @@ private async IAsyncEnumerable GetMissingRightRes } /// - public virtual async Task AddToToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, + public virtual async Task AddToToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new @@ -385,7 +390,7 @@ public virtual async Task AddToToManyRelationshipAsync(TId leftId, string relati } } - private async Task RemoveExistingIdsFromRelationshipRightSideAsync(HasManyAttribute hasManyRelationship, TId leftId, + private async Task RemoveExistingIdsFromRelationshipRightSideAsync(HasManyAttribute hasManyRelationship, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) { TResource leftResource = await GetForHasManyUpdateAsync(hasManyRelationship, leftId, rightResourceIds, cancellationToken); @@ -398,8 +403,8 @@ private async Task RemoveExistingIdsFromRelationshipRightSideAsync(Ha return leftResource; } - private async Task GetForHasManyUpdateAsync(HasManyAttribute hasManyRelationship, TId leftId, ISet rightResourceIds, - CancellationToken cancellationToken) + private async Task GetForHasManyUpdateAsync(HasManyAttribute hasManyRelationship, [DisallowNull] TId leftId, + ISet rightResourceIds, CancellationToken cancellationToken) { QueryLayer queryLayer = _queryLayerComposer.ComposeForHasMany(hasManyRelationship, leftId, rightResourceIds); var leftResource = await _repositoryAccessor.GetForUpdateAsync(queryLayer, cancellationToken); @@ -438,7 +443,7 @@ private async Task GetForHasManyUpdateAsync(HasManyAttribute hasManyR } /// - public virtual async Task UpdateAsync(TId id, TResource resource, CancellationToken cancellationToken) + public virtual async Task UpdateAsync([DisallowNull] TId id, TResource resource, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -481,7 +486,7 @@ private async Task GetForHasManyUpdateAsync(HasManyAttribute hasManyR } /// - public virtual async Task SetRelationshipAsync(TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) + public virtual async Task SetRelationshipAsync([DisallowNull] TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -519,7 +524,7 @@ public virtual async Task SetRelationshipAsync(TId leftId, string relationshipNa } /// - public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToken) + public virtual async Task DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new { @@ -552,7 +557,7 @@ public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToke } /// - public virtual async Task RemoveFromToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, + public virtual async Task RemoveFromToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken) { _traceWriter.LogMethodStart(new @@ -581,7 +586,7 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TId leftId, string r await _repositoryAccessor.RemoveFromToManyRelationshipAsync(resourceFromDatabase, effectiveRightResourceIds, cancellationToken); } - protected async Task GetPrimaryResourceByIdAsync(TId id, TopFieldSelection fieldSelection, CancellationToken cancellationToken) + protected async Task GetPrimaryResourceByIdAsync([DisallowNull] TId id, TopFieldSelection fieldSelection, CancellationToken cancellationToken) { TResource? primaryResource = await GetPrimaryResourceByIdOrDefaultAsync(id, fieldSelection, cancellationToken); AssertPrimaryResourceExists(primaryResource); @@ -589,7 +594,8 @@ protected async Task GetPrimaryResourceByIdAsync(TId id, TopFieldSele return primaryResource; } - private async Task GetPrimaryResourceByIdOrDefaultAsync(TId id, TopFieldSelection fieldSelection, CancellationToken cancellationToken) + private async Task GetPrimaryResourceByIdOrDefaultAsync([DisallowNull] TId id, TopFieldSelection fieldSelection, + CancellationToken cancellationToken) { AssertPrimaryResourceTypeInJsonApiRequestIsNotNull(_request.PrimaryResourceType); @@ -599,7 +605,7 @@ protected async Task GetPrimaryResourceByIdAsync(TId id, TopFieldSele return primaryResources.SingleOrDefault(); } - protected async Task GetPrimaryResourceForUpdateAsync(TId id, CancellationToken cancellationToken) + protected async Task GetPrimaryResourceForUpdateAsync([DisallowNull] TId id, CancellationToken cancellationToken) { AssertPrimaryResourceTypeInJsonApiRequestIsNotNull(_request.PrimaryResourceType); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenantResourceService.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenantResourceService.cs index 9918cbaff3..89a2d497dd 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenantResourceService.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/MultiTenantResourceService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; @@ -45,21 +46,21 @@ protected override async Task InitializeResourceAsync(TResource resourceForDatab return await base.CreateAsync(resource, cancellationToken); } - public override async Task UpdateAsync(TId id, TResource resource, CancellationToken cancellationToken) + public override async Task UpdateAsync([DisallowNull] TId id, TResource resource, CancellationToken cancellationToken) { await AssertResourcesToAssignInRelationshipsExistAsync(resource, cancellationToken); return await base.UpdateAsync(id, resource, cancellationToken); } - public override async Task SetRelationshipAsync(TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) + public override async Task SetRelationshipAsync([DisallowNull] TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) { await AssertRightResourcesExistAsync(rightValue, cancellationToken); await base.SetRelationshipAsync(leftId, relationshipName, rightValue, cancellationToken); } - public override async Task AddToToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, + public override async Task AddToToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken) { _ = await GetPrimaryResourceByIdAsync(leftId, TopFieldSelection.OnlyIdAttribute, cancellationToken); @@ -68,7 +69,7 @@ public override async Task AddToToManyRelationshipAsync(TId leftId, string relat await base.AddToToManyRelationshipAsync(leftId, relationshipName, rightResourceIds, cancellationToken); } - public override async Task DeleteAsync(TId id, CancellationToken cancellationToken) + public override async Task DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken) { _ = await GetPrimaryResourceByIdAsync(id, TopFieldSelection.OnlyIdAttribute, cancellationToken); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionAwareResourceService.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionAwareResourceService.cs index 9baef849ed..8f1ce719e8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionAwareResourceService.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/SoftDeletionAwareResourceService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; @@ -37,7 +38,7 @@ public class SoftDeletionAwareResourceService( return await base.CreateAsync(resource, cancellationToken); } - public override async Task UpdateAsync(TId id, TResource resource, CancellationToken cancellationToken) + public override async Task UpdateAsync([DisallowNull] TId id, TResource resource, CancellationToken cancellationToken) { if (_targetedFields.Relationships.Any(relationship => IsSoftDeletable(relationship.RightType.ClrType))) { @@ -47,7 +48,7 @@ public class SoftDeletionAwareResourceService( return await base.UpdateAsync(id, resource, cancellationToken); } - public override async Task SetRelationshipAsync(TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) + public override async Task SetRelationshipAsync([DisallowNull] TId leftId, string relationshipName, object? rightValue, CancellationToken cancellationToken) { if (IsSoftDeletable(_request.Relationship!.RightType.ClrType)) { @@ -57,7 +58,7 @@ public override async Task SetRelationshipAsync(TId leftId, string relationshipN await base.SetRelationshipAsync(leftId, relationshipName, rightValue, cancellationToken); } - public override async Task AddToToManyRelationshipAsync(TId leftId, string relationshipName, ISet rightResourceIds, + public override async Task AddToToManyRelationshipAsync([DisallowNull] TId leftId, string relationshipName, ISet rightResourceIds, CancellationToken cancellationToken) { if (IsSoftDeletable(typeof(TResource))) @@ -73,7 +74,7 @@ public override async Task AddToToManyRelationshipAsync(TId leftId, string relat await base.AddToToManyRelationshipAsync(leftId, relationshipName, rightResourceIds, cancellationToken); } - public override async Task DeleteAsync(TId id, CancellationToken cancellationToken) + public override async Task DeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken) { if (IsSoftDeletable(typeof(TResource))) { @@ -85,7 +86,7 @@ public override async Task DeleteAsync(TId id, CancellationToken cancellationTok } } - private async Task SoftDeleteAsync(TId id, CancellationToken cancellationToken) + private async Task SoftDeleteAsync([DisallowNull] TId id, CancellationToken cancellationToken) { TResource resourceFromDatabase = await GetPrimaryResourceForUpdateAsync(id, cancellationToken); From ee41f122807febf75afc7d0300e34337ffd1fa4d Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:25:43 +0200 Subject: [PATCH 4/5] Increment version to 5.6.0 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 534f9bb7af..860217f52e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,6 @@ false $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.5.2 + 5.6.0 From 7ed01d2c7f94c053084ecc0afec85a9104c86621 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 21:28:22 +0200 Subject: [PATCH 5/5] Increment version to 5.6.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 860217f52e..375ee5066f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,6 @@ false $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.6.0 + 5.6.1