Skip to content

Commit

Permalink
Add analytics tracking for UpdateTenant, DeleteTenant, UpdateUser, an…
Browse files Browse the repository at this point in the history
…d DeleteTenant
  • Loading branch information
tjementum committed Dec 5, 2023
1 parent dc73d00 commit 827e98e
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using FluentValidation;
using PlatformPlatform.SharedKernel.ApplicationCore.Cqrs;
using PlatformPlatform.SharedKernel.ApplicationCore.Tracking;

namespace PlatformPlatform.AccountManagement.Application.Tenants;

public sealed record DeleteTenantCommand(TenantId Id) : ICommand, IRequest<Result>;

[UsedImplicitly]
public sealed class DeleteTenantHandler(ITenantRepository tenantRepository)
public sealed class DeleteTenantHandler(
ITenantRepository tenantRepository,
IAnalyticEventsCollector analyticEventsCollector
)
: IRequestHandler<DeleteTenantCommand, Result>
{
public async Task<Result> Handle(DeleteTenantCommand command, CancellationToken cancellationToken)
Expand All @@ -15,6 +19,16 @@ public async Task<Result> Handle(DeleteTenantCommand command, CancellationToken
if (tenant is null) return Result.NotFound($"Tenant with id '{command.Id}' not found.");

tenantRepository.Remove(tenant);

analyticEventsCollector.CollectEvent(
"TenantDeleted",
new Dictionary<string, string>
{
{ "Tenant_Id", tenant.Id.ToString() },
{ "Event_TenantState", tenant.State.ToString() }
}
);

return Result.Success();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PlatformPlatform.SharedKernel.ApplicationCore.Cqrs;
using PlatformPlatform.SharedKernel.ApplicationCore.Tracking;

namespace PlatformPlatform.AccountManagement.Application.Tenants;

Expand All @@ -13,7 +14,10 @@ public sealed record UpdateTenantCommand : ICommand, ITenantValidation, IRequest
}

[UsedImplicitly]
public sealed class UpdateTenantHandler(ITenantRepository tenantRepository)
public sealed class UpdateTenantHandler(
ITenantRepository tenantRepository,
IAnalyticEventsCollector analyticEventsCollector
)
: IRequestHandler<UpdateTenantCommand, Result>
{
public async Task<Result> Handle(UpdateTenantCommand command, CancellationToken cancellationToken)
Expand All @@ -23,6 +27,12 @@ public async Task<Result> Handle(UpdateTenantCommand command, CancellationToken

tenant.Update(command.Name, command.Phone);
tenantRepository.Update(tenant);

analyticEventsCollector.CollectEvent(
"TenantUpdated",
new Dictionary<string, string> { { "Tenant_Id", command.Id.ToString() } }
);

return Result.Success();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
using PlatformPlatform.SharedKernel.ApplicationCore.Cqrs;
using PlatformPlatform.SharedKernel.ApplicationCore.Tracking;

namespace PlatformPlatform.AccountManagement.Application.Users;

public sealed record DeleteUserCommand(UserId Id) : ICommand, IRequest<Result>;

[UsedImplicitly]
public sealed class DeleteUserHandler(IUserRepository userRepository) : IRequestHandler<DeleteUserCommand, Result>
public sealed class DeleteUserHandler(
IUserRepository userRepository,
IAnalyticEventsCollector analyticEventsCollector
) : IRequestHandler<DeleteUserCommand, Result>
{
public async Task<Result> Handle(DeleteUserCommand command, CancellationToken cancellationToken)
{
var user = await userRepository.GetByIdAsync(command.Id, cancellationToken);
if (user is null) return Result.NotFound($"User with id '{command.Id}' not found.");

userRepository.Remove(user);

analyticEventsCollector.CollectEvent("UserDeleted");
return Result.Success();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PlatformPlatform.SharedKernel.ApplicationCore.Cqrs;
using PlatformPlatform.SharedKernel.ApplicationCore.Tracking;

namespace PlatformPlatform.AccountManagement.Application.Users;

Expand All @@ -13,7 +14,10 @@ public sealed record UpdateUserCommand : ICommand, IUserValidation, IRequest<Res
}

[UsedImplicitly]
public sealed class UpdateUserHandler(IUserRepository userRepository) : IRequestHandler<UpdateUserCommand, Result>
public sealed class UpdateUserHandler(
IUserRepository userRepository,
IAnalyticEventsCollector analyticEventsCollector
) : IRequestHandler<UpdateUserCommand, Result>
{
public async Task<Result> Handle(UpdateUserCommand command, CancellationToken cancellationToken)
{
Expand All @@ -22,6 +26,8 @@ public async Task<Result> Handle(UpdateUserCommand command, CancellationToken ca

user.Update(command.Email, command.UserRole);
userRepository.Update(user);

analyticEventsCollector.CollectEvent("UserUpdated");
return Result.Success();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public async Task CreateTenant_WhenInvalid_ShouldReturnBadRequest()
new ErrorDetail("Email", "Email must be in a valid format and no longer than 100 characters.")
};
await EnsureErrorStatusCode(response, HttpStatusCode.BadRequest, expectedErrors);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -141,6 +143,8 @@ public async Task CreateTenant_WhenTenantExists_ShouldReturnBadRequest()
new ErrorDetail("Subdomain", "The subdomain is not available.")
};
await EnsureErrorStatusCode(response, HttpStatusCode.BadRequest, expectedErrors);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -155,6 +159,10 @@ public async Task UpdateTenant_WhenValid_ShouldUpdateTenant()

// Assert
EnsureSuccessWithEmptyHeaderAndLocation(response);

AnalyticEventsCollectorSpy.CollectedEvents.Count.Should().Be(1);
AnalyticEventsCollectorSpy.CollectedEvents.Count(e => e.Name == "TenantUpdated").Should().Be(1);
AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeTrue();
}

[Fact]
Expand All @@ -176,6 +184,8 @@ public async Task UpdateTenant_WhenInvalid_ShouldReturnBadRequest()
new ErrorDetail("Phone", "Phone must be in a valid format and no longer than 20 characters.")
};
await EnsureErrorStatusCode(response, HttpStatusCode.BadRequest, expectedErrors);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -194,6 +204,8 @@ await EnsureErrorStatusCode(
HttpStatusCode.NotFound,
$"Tenant with id '{unknownTenantId}' not found."
);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -211,6 +223,8 @@ await EnsureErrorStatusCode(
HttpStatusCode.NotFound,
$"Tenant with id '{unknownTenantId}' not found."
);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -219,13 +233,16 @@ public async Task DeleteTenant_WhenTenantHasUsers_ShouldReturnBadRequest()
// Act
var existingTenantId = DatabaseSeeder.Tenant1.Id;
var response = await TestHttpClient.DeleteAsync($"/api/tenants/{existingTenantId}");
AnalyticEventsCollectorSpy.Reset();

// Assert
var expectedErrors = new[]
{
new ErrorDetail("Id", "All users must be deleted before the tenant can be deleted.")
};
await EnsureErrorStatusCode(response, HttpStatusCode.BadRequest, expectedErrors);

AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeFalse();
}

[Fact]
Expand All @@ -235,12 +252,17 @@ public async Task DeleteTenant_WhenTenantHasNoUsers_ShouldDeleteTenant()
var existingTenantId = DatabaseSeeder.Tenant1.Id;
var existingUserId = DatabaseSeeder.User1.Id;
_ = await TestHttpClient.DeleteAsync($"/api/users/{existingUserId}");
AnalyticEventsCollectorSpy.Reset();

// Act
var response = await TestHttpClient.DeleteAsync($"/api/tenants/{existingTenantId}");

// Assert
EnsureSuccessWithEmptyHeaderAndLocation(response);
Connection.RowExists("Tenants", existingTenantId).Should().BeFalse();

AnalyticEventsCollectorSpy.CollectedEvents.Count.Should().Be(1);
AnalyticEventsCollectorSpy.CollectedEvents.Count(e => e.Name == "TenantDeleted").Should().Be(1);
AnalyticEventsCollectorSpy.AreAllEventsDispatched.Should().BeTrue();
}
}

0 comments on commit 827e98e

Please sign in to comment.