-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[EC-508] SCIM CQRS Refactor - Users/Delete #2261
Merged
r-tome
merged 16 commits into
feature/scim-cqrs
from
EC-539-scim-cqrs-refactor-users-delete
Oct 17, 2022
Merged
Changes from 3 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
148e080
[EC-390] Added Scim.Test unit tests project
r-tome b925cc7
[EC-390] Added ConflictException type. Updated BadRequestException to…
r-tome 976ac23
[EC-539] Implemented CQRS for Users Delete and added unit tests
r-tome 3c7a42c
[EC-508] Created ScimServiceCollectionExtensions
r-tome dee798c
[EC-508] Created ExceptionHandlerFilterAttribute on SCIM project
r-tome a67ca60
Merge branch 'master' into EC-539-scim-cqrs-refactor-users-delete
r-tome 92d32d4
[EC-508] Removed unneeded model from DeleteUserCommand. Removed unnee…
r-tome 1bb485a
[EC-508] Removed Bit.Scim.Models dependency from DeleteUserCommandTests
r-tome 1a07044
[EC-508] Deleted 'DeleteUserCommand' from SCIM; Created commands on C…
r-tome 7144a25
[EC-508] Changed DeleteOrganizationUserCommand back to using IOrganiz…
r-tome d677877
Merge branch 'master' into EC-539-scim-cqrs-refactor-users-delete
r-tome defc466
[EC-508] Fixed DeleteOrganizationUserCommand unit tests
r-tome 9f0b671
Merge branch 'EC-539-scim-cqrs-refactor-users-delete' of https://gith…
r-tome 78ef4f2
[EC-508] Remove unneeded obsolete comments. Update DeleteUserAsync Ob…
r-tome e69cb62
[EC-508] Move DeleteOrganizationUserCommand to OrganizationFeatures f…
r-tome de90c6b
Merge branch 'feature/scim-cqrs' into EC-539-scim-cqrs-refactor-users…
r-tome File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
bitwarden_license/src/Scim/Commands/Users/DeleteUserCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Bit.Core.Exceptions; | ||
using Bit.Core.Repositories; | ||
using Bit.Core.Services; | ||
using Bit.Scim.Commands.Users.Interfaces; | ||
using Bit.Scim.Models; | ||
|
||
namespace Bit.Scim.Commands.Users; | ||
|
||
public class DeleteUserCommand : IDeleteUserCommand | ||
{ | ||
private readonly IOrganizationService _organizationService; | ||
private readonly IOrganizationUserRepository _organizationUserRepository; | ||
|
||
public DeleteUserCommand( | ||
IOrganizationService organizationService, | ||
IOrganizationUserRepository organizationUserRepository) | ||
{ | ||
_organizationService = organizationService; | ||
_organizationUserRepository = organizationUserRepository; | ||
} | ||
|
||
public async Task DeleteUserAsync(Guid organizationId, Guid id, ScimUserRequestModel model) | ||
{ | ||
var orgUser = await _organizationUserRepository.GetByIdAsync(id); | ||
if (orgUser == null || orgUser.OrganizationId != organizationId) | ||
{ | ||
throw new NotFoundException("User not found."); | ||
} | ||
|
||
await _organizationService.DeleteUserAsync(organizationId, id, null); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
bitwarden_license/src/Scim/Commands/Users/Interfaces/IDeleteUserCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using Bit.Scim.Models; | ||
|
||
namespace Bit.Scim.Commands.Users.Interfaces; | ||
|
||
public interface IDeleteUserCommand | ||
{ | ||
Task DeleteUserAsync(Guid organizationId, Guid id, ScimUserRequestModel model); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
bitwarden_license/test/Scim.Test/Commands/Users/DeleteUserCommandTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using Bit.Core.Entities; | ||
using Bit.Core.Exceptions; | ||
using Bit.Core.Repositories; | ||
using Bit.Core.Services; | ||
using Bit.Scim.Commands.Users; | ||
using Bit.Scim.Models; | ||
using Bit.Test.Common.AutoFixture; | ||
using Bit.Test.Common.AutoFixture.Attributes; | ||
using NSubstitute; | ||
using Xunit; | ||
|
||
namespace Bit.Scim.Test.Commands.Users; | ||
|
||
[SutProviderCustomize] | ||
public class DeleteUserCommandTests | ||
{ | ||
[Theory] | ||
[BitAutoData] | ||
public async Task DeleteUser_Success(SutProvider<DeleteUserCommand> sutProvider, Guid organizationId, Guid organizationUserId, ScimUserRequestModel model) | ||
{ | ||
sutProvider.GetDependency<IOrganizationUserRepository>() | ||
.GetByIdAsync(organizationUserId) | ||
.Returns(new OrganizationUser | ||
{ | ||
Id = organizationUserId, | ||
OrganizationId = organizationId | ||
}); | ||
|
||
await sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, model); | ||
|
||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetByIdAsync(organizationUserId); | ||
await sutProvider.GetDependency<IOrganizationService>().Received(1).DeleteUserAsync(organizationId, organizationUserId, null); | ||
} | ||
|
||
[Theory] | ||
[BitAutoData] | ||
public async Task DeleteUser_NotFound_Throws(SutProvider<DeleteUserCommand> sutProvider, Guid organizationId, Guid organizationUserId, ScimUserRequestModel model) | ||
{ | ||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, model)); | ||
} | ||
|
||
[Theory] | ||
[BitAutoData] | ||
public async Task DeleteUser_MismatchingOrganizationId_Throws(SutProvider<DeleteUserCommand> sutProvider, Guid organizationId, Guid organizationUserId, ScimUserRequestModel model) | ||
{ | ||
sutProvider.GetDependency<IOrganizationUserRepository>() | ||
.GetByIdAsync(organizationUserId) | ||
.Returns(new OrganizationUser | ||
{ | ||
Id = organizationUserId, | ||
OrganizationId = Guid.NewGuid() | ||
}); | ||
|
||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, model)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNetTestSdkVersion)" /> | ||
<PackageReference Include="xunit" Version="$(XUnitVersion)" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioVersion)"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorVersion)"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="NSubstitute" Version="$(NSubstitueVersion)" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Scim\Scim.csproj" /> | ||
<ProjectReference Include="..\..\..\test\Common\Common.csproj" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic already exists within the
organizationService.DeleteUserAsync
which makes this a bit repetitive. Should we check what error we get back from the OrganizationService and handle it that way instead?However by removing that logic we've essentially made this command just a direct call to the service. At which point the commands benefit is slightly unclear. (Other than acting as a potential basis for in the future extracting the
DeleteUserAsync
completely into a command.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MGibson1 curious what your thoughts are, in my opinion we can either.
Core
, and deprecate the method on the service. The thinking being that we will eventually want to migrate over to use the command instead of the service.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote 2, personally.
DeleteUserAsync
can be a command inCore
which can be called from wherever, SCIM might just have some error handling code around it, but wouldn't itself implement a separate query. That makes this refactor more useful. Thoughts @r-tome?Although @Hinton my question would then be how to handle the call to
OrganizationService.HasConfirmedOwnersExceptAsync
inOrganizationService.DeleteUserAsync
. Is that considered a query by itself?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 sounds good to me although it can turn out to be a bigger refactor than anticipated.
@eliykat I second that question on
OrganizationService.HasConfirmedOwnersExceptAsync
and alsoOrganizationService.DeleteAndPushUserRegistrationAsync
. Should I create a new query and command and mark the old methods as obsolete?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, just having the command call the service would be enough. Once everything uses the command we can extract it. That saves us having two identical implementations.
The alternative is to refactor the organization service to call the command, and ensure we only have a single implementation. If code is generic enough it's fine to continue having it in the service. We should try and avoid scope creep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll keep this simple and make the command just call the service for now, we can refactor this later when we switch everything to use commands.
There is a slight difference between the implementations. When the user is not found on
OrganizationService
it returns aBadRequestException
whereas SCIM throws aNotFoundException
. How do I handle this? Change the thrown exception type toNotFoundException
for both or handle that insideExceptionHandlerFilterAttribute
on SCIM?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've up the check back on the command even though it seems redundant just because it's easier to throw a different exception type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, it's a bit redundant but it was also already here, and it ensures that we comply with the SCIM protocol without causing regressions in other clients. From Oscar's comments above I take it that we don't want to expand the scope of this refactor too much.