Skip to content
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

Admin API for sending announcements to specific identities #1034

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
5435a16
feat: added optional property recipients and validation rule
devx247 Jan 8, 2025
60208d0
fix: declared the property Recipients as nullable
devx247 Jan 8, 2025
ccc0caf
fix: validation rule of Recipients
devx247 Jan 8, 2025
25d9bc8
fix: validation of property Recipients
devx247 Jan 8, 2025
095dd29
feat: added AnnouncementRecipient + Migration + Handler update
devx247 Jan 10, 2025
244b257
feat: added AnnouncementRecipient Migration
devx247 Jan 13, 2025
c2de326
feat: added announcement-recipients, updated outgoing/incoming domain…
devx247 Jan 13, 2025
4cdda3b
feat: return recipients in admin-api on get request
devx247 Jan 14, 2025
73738a9
feat: added Announcement Recipient IdentityDeleter
devx247 Jan 14, 2025
695ca99
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
devx247 Jan 14, 2025
2a641b5
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
mergify[bot] Jan 14, 2025
c10da3f
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
mergify[bot] Jan 15, 2025
42269c3
chore: removed pubspec.lock
devx247 Jan 15, 2025
003e408
Merge remote-tracking branch 'origin/ABL-174-admin-api-send-announcem…
devx247 Jan 15, 2025
9277fcb
fix: fixed AnnouncementRecipient + PushService usage per PnsHandle
devx247 Jan 15, 2025
f5261ba
test: added CreateAnnouncement Handler Tests
devx247 Jan 16, 2025
9096229
test: added AnnouncementRecipient Anonymize IdentityDeleter Tests
devx247 Jan 16, 2025
6e170dc
test: added AnonymizeRecipient tests
devx247 Jan 16, 2025
9328f7f
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
tnotheis Jan 17, 2025
014dd0a
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
devx247 Jan 20, 2025
81f9fc6
fix: updated docker file
devx247 Jan 20, 2025
9b8f0f8
fix: updated docker-file
devx247 Jan 20, 2025
9af11c3
chore: added debug-logging
devx247 Jan 20, 2025
31d33a7
chore: added some debug statement
devx247 Jan 20, 2025
dda86bc
chore: debug - check database-migrator logs
devx247 Jan 20, 2025
b46d207
chore: debug - fixed the check database-migrator-test log cmd
devx247 Jan 20, 2025
c9c4e18
chore: added new migration as error seen in logs regarding Announceme…
devx247 Jan 20, 2025
2df11a5
fix: added announcement-recipient migration for sqlserver + postgres db
devx247 Jan 21, 2025
5829632
fix: another attempt to get the database-migrator-test logs
devx247 Jan 21, 2025
b48c27c
chore: try to log exiting ci-seed-database-1 container
devx247 Jan 21, 2025
3324fcf
chore: try to log exiting ci-seed-database-1 container
devx247 Jan 21, 2025
779d2df
fix: replaced DistinctBy (can't be translated to SQL by EF Core)
devx247 Jan 21, 2025
e155c15
chore: removed debug log
devx247 Jan 22, 2025
0cf641c
fix: added AnnouncementsModule
devx247 Jan 22, 2025
17dbb0c
fix: added missing DOCKERFILE project reference
devx247 Jan 22, 2025
44be2f6
fix: added missing DOCKERFILE project reference
devx247 Jan 22, 2025
6c344d2
fix: updated regarding PR remarks
devx247 Jan 22, 2025
7423ed5
fix: use change-tracker announcements
devx247 Jan 22, 2025
91fb134
fix: updated regarding PR remarks
devx247 Jan 23, 2025
a4f3236
test: new AnonymizeRecipient Handler UnitTest
devx247 Jan 23, 2025
fafb1d6
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
devx247 Jan 23, 2025
aac51b7
chore: added ValidId check
devx247 Jan 23, 2025
297a235
fix: failed test
devx247 Jan 23, 2025
0f7ccd0
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
mergify[bot] Jan 27, 2025
9f18a45
chore: cleanup
tnotheis Jan 27, 2025
2455ec7
fix: remove didDomainName from appsettings.json files and add it to a…
tnotheis Jan 27, 2025
af3453f
feat: delete recipients instead of anonymizing them
tnotheis Jan 27, 2025
d62ea22
Merge branch 'main' into ABL-174-admin-api-send-announcements-to-spec…
mergify[bot] Jan 28, 2025
1889c9c
refactor: simplify CreateAnnouncement Handler
tnotheis Jan 28, 2025
6ab554a
chore: add `FromBody` attribute
tnotheis Jan 28, 2025
2b6ce10
refactor: simplify application logic by adding a default value to the…
tnotheis Jan 28, 2025
2f7a92a
fix: don't catch and rethrow exceptions in SseServerClient
tnotheis Jan 28, 2025
97bd30a
refactor: simplify validation
tnotheis Jan 28, 2025
7d4f08a
fix: fix migration errors
tnotheis Jan 28, 2025
088ad5f
fix: return simple string instead of an object in AnnouncementDTO.cs
tnotheis Jan 28, 2025
72192c9
chore: delete redundant tests
tnotheis Jan 28, 2025
66169bc
chore: delete unused repository method
tnotheis Jan 28, 2025
0c49df8
chore: don't unclude navigation properties when deleting recipients
tnotheis Jan 28, 2025
8b7a410
chore: cleanup
tnotheis Jan 28, 2025
7992d91
refactor: move extension method to correct place
tnotheis Jan 28, 2025
25d9748
refactor: simplify AnnouncementCreatedDomainEventHandler
tnotheis Jan 28, 2025
e04ee3d
chore: restore original order of members of PushService
tnotheis Jan 28, 2025
2e69771
refactor: filter for distinct handles in PushService instead of in th…
tnotheis Jan 28, 2025
78d6d91
test: fix invalidId tests
tnotheis Jan 28, 2025
26ccbc9
chore: remove redundant query parameters from bruno files
tnotheis Jan 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Applications/AdminApi/http/Announcements/Announcements.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
meta {
name: /Announcements
type: http
seq: 1
}

post {
url: {{baseUrl}}/Announcements
body: json
auth: none
}

body:json {
{
"severity": 1,
"texts": [
{
"language": "en",
"title": "System Maintenance V2",
"body": "The system will be undergoing maintenance on Saturday."
}
],
"expiresAt": "2023-12-31T23:59:59Z",
"recipients": [
"did:e:localhost:dids:8234cca0160ff05c785636",
"did:e:localhost:dids:5b8640b14cc9796fbf8d0d"
]
}
}
11 changes: 11 additions & 0 deletions Applications/AdminApi/http/Announcements/List Announcements.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
meta {
name: /Announcements
type: http
seq: 2
}

get {
url: {{baseUrl}}/Announcements
body: none
auth: none
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public AnnouncementsController(IMediator mediator) : base(mediator)
}

[HttpPost]
public async Task<IActionResult> CreateAnnouncement(CreateAnnouncementCommand request, CancellationToken cancellationToken)
public async Task<IActionResult> CreateAnnouncement([FromBody] CreateAnnouncementCommand request, CancellationToken cancellationToken)
{
var response = await _mediator.Send(request, cancellationToken);
return Created(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
}
},
"Modules": {
"Announcements": {
"Infrastructure": {
"SqlDatabase": {
"Provider": "Postgres",
"ConnectionString": "User ID=postgres;Password=admin;Server=localhost;Port=5432;Database=enmeshed;" // postgres
// "ConnectionString": "Server=localhost;Database=enmeshed;User Id=sa;Password=Passw0rd;TrustServerCertificate=True" // sqlserver
}
}
},
"Quotas": {
"Infrastructure": {
"SqlDatabase": {
Expand Down
2 changes: 0 additions & 2 deletions Applications/ConsumerApi/src/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
},
"Devices": {
"Application": {
"DidDomainName": "localhost",
"Pagination": {
"DefaultPageSize": 50,
"MaxPageSize": 200
Expand All @@ -79,7 +78,6 @@
},
"Messages": {
"Application": {
"DidDomainName": "localhost",
"MaxNumberOfUnreceivedMessagesFromOneSender": 20,
"Pagination": {
"DefaultPageSize": 50,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
meta {
name: /Announcements
type: http
seq: 1
}

get {
url: {{baseUrl}}/Announcements
body: none
auth: inherit
}

params:query {
language: en
}
6 changes: 3 additions & 3 deletions Applications/ConsumerApi/src/http/Tokens/List Tokens.bru
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ get {
}

params:query {
~ids: TOKsjPynl0FHYJzHnkIo
~PageNumber: 1
~PageSize: 1
ids: TOKsjPynl0FHYJzHnkIo
PageNumber: 1
PageSize: 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
}
},
"Modules": {
"Announcements": {
"Infrastructure": {
"SqlDatabase": {
"Provider": "Postgres",
"ConnectionString": "User ID=postgres;Password=admin;Server=localhost;Port=5432;Database=enmeshed;" // postgres
// "ConnectionString": "Server=localhost;Database=enmeshed;User Id=sa;Password=Passw0rd;TrustServerCertificate=True" // sqlserver
}
}
},
"Challenges": {
"Infrastructure": {
"SqlDatabase": {
Expand Down Expand Up @@ -80,6 +89,9 @@
}
},
"Tokens": {
"Application": {
"DidDomainName": "localhost"
},
"Infrastructure": {
"SqlDatabase": {
"Provider": "Postgres",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,33 @@ WORKDIR /src
COPY ["Directory.Build.props", "."]
COPY ["Applications/IdentityDeletionJobs/src/Job.IdentityDeletion/Job.IdentityDeletion.csproj", "Applications/IdentityDeletionJobs/src/Job.IdentityDeletion/"]
COPY ["BuildingBlocks/src/BuildingBlocks.API/BuildingBlocks.API.csproj", "BuildingBlocks/src/BuildingBlocks.API/"]
COPY ["Modules/Devices/src/Devices.Domain/Devices.Domain.csproj", "Modules/Devices/src/Devices.Domain/"]
COPY ["BuildingBlocks/src/BuildingBlocks.Domain/BuildingBlocks.Domain.csproj", "BuildingBlocks/src/BuildingBlocks.Domain/"]
COPY ["BuildingBlocks/src/Tooling/Tooling.csproj", "BuildingBlocks/src/Tooling/"]
COPY ["BuildingBlocks/src/DevelopmentKit.Identity/DevelopmentKit.Identity.csproj", "BuildingBlocks/src/DevelopmentKit.Identity/"]
COPY ["Modules/Devices/src/Devices.Infrastructure/Devices.Infrastructure.csproj", "Modules/Devices/src/Devices.Infrastructure/"]
COPY ["BuildingBlocks/src/BuildingBlocks.Infrastructure/BuildingBlocks.Infrastructure.csproj", "BuildingBlocks/src/BuildingBlocks.Infrastructure/"]
COPY ["BuildingBlocks/src/BuildingBlocks.Application.Abstractions/BuildingBlocks.Application.Abstractions.csproj", "BuildingBlocks/src/BuildingBlocks.Application.Abstractions/"]
COPY ["Modules/Devices/src/Devices.Application/Devices.Application.csproj", "Modules/Devices/src/Devices.Application/"]
COPY ["BuildingBlocks/src/BuildingBlocks.Application/BuildingBlocks.Application.csproj", "BuildingBlocks/src/BuildingBlocks.Application/"]
COPY ["Common/src/Common.Infrastructure/Common.Infrastructure.csproj", "Common/src/Common.Infrastructure/"]
COPY ["BuildingBlocks/src/Crypto/Crypto.csproj", "BuildingBlocks/src/Crypto/"]
COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"]
COPY ["Modules/Announcements/src/Announcements.Domain/Announcements.Domain.csproj", "Modules/Announcements/src/Announcements.Domain/"]
COPY ["Modules/Announcements/src/Announcements.Application/Announcements.Application.csproj", "Modules/Announcements/src/Announcements.Application/"]
COPY ["Modules/Announcements/src/Announcements.ConsumerApi/Announcements.ConsumerApi.csproj", "Modules/Announcements/src/Announcements.ConsumerApi/"]
COPY ["Modules/Announcements/src/Announcements.Infrastructure.Database.Postgres/Announcements.Infrastructure.Database.Postgres.csproj", "Modules/Announcements/src/Announcements.Infrastructure.Database.Postgres/"]
COPY ["Modules/Announcements/src/Announcements.Infrastructure/Announcements.Infrastructure.csproj", "Modules/Announcements/src/Announcements.Infrastructure/"]
COPY ["Modules/Announcements/src/Announcements.Infrastructure.Database.SqlServer/Announcements.Infrastructure.Database.SqlServer.csproj", "Modules/Announcements/src/Announcements.Infrastructure.Database.SqlServer/"]
COPY ["Modules/Challenges/src/Challenges.Application/Challenges.Application.csproj", "Modules/Challenges/src/Challenges.Application/"]
COPY ["Modules/Challenges/src/Challenges.Domain/Challenges.Domain.csproj", "Modules/Challenges/src/Challenges.Domain/"]
COPY ["Modules/Challenges/src/Challenges.ConsumerApi/Challenges.ConsumerApi.csproj", "Modules/Challenges/src/Challenges.ConsumerApi/"]
COPY ["Modules/Challenges/src/Challenges.Infrastructure.Database.Postgres/Challenges.Infrastructure.Database.Postgres.csproj", "Modules/Challenges/src/Challenges.Infrastructure.Database.Postgres/"]
COPY ["Modules/Challenges/src/Challenges.Infrastructure/Challenges.Infrastructure.csproj", "Modules/Challenges/src/Challenges.Infrastructure/"]
COPY ["Modules/Challenges/src/Challenges.Infrastructure.Database.SqlServer/Challenges.Infrastructure.Database.SqlServer.csproj", "Modules/Challenges/src/Challenges.Infrastructure.Database.SqlServer/"]
COPY ["Modules/Devices/src/Devices.Domain/Devices.Domain.csproj", "Modules/Devices/src/Devices.Domain/"]
COPY ["Modules/Devices/src/Devices.Infrastructure/Devices.Infrastructure.csproj", "Modules/Devices/src/Devices.Infrastructure/"]
COPY ["Modules/Devices/src/Devices.Application/Devices.Application.csproj", "Modules/Devices/src/Devices.Application/"]
COPY ["Modules/Devices/src/Devices.ConsumerApi/Devices.ConsumerApi.csproj", "Modules/Devices/src/Devices.ConsumerApi/"]
COPY ["Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Devices.Infrastructure.Database.Postgres.csproj", "Modules/Devices/src/Devices.Infrastructure.Database.Postgres/"]
COPY ["Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Devices.Infrastructure.Database.SqlServer.csproj", "Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/"]
COPY ["Modules/Files/src/Files.Application/Files.Application.csproj", "Modules/Files/src/Files.Application/"]
COPY ["Modules/Files/src/Files.Domain/Files.Domain.csproj", "Modules/Files/src/Files.Domain/"]
COPY ["Modules/Files/src/Files.ConsumerApi/Files.ConsumerApi.csproj", "Modules/Files/src/Files.ConsumerApi/"]
Expand Down Expand Up @@ -62,9 +71,6 @@ COPY ["Modules/Tokens/src/Tokens.ConsumerApi/Tokens.ConsumerApi.csproj", "Module
COPY ["Modules/Tokens/src/Tokens.Infrastructure.Database.Postgres/Tokens.Infrastructure.Database.Postgres.csproj", "Modules/Tokens/src/Tokens.Infrastructure.Database.Postgres/"]
COPY ["Modules/Tokens/src/Tokens.Infrastructure/Tokens.Infrastructure.csproj", "Modules/Tokens/src/Tokens.Infrastructure/"]
COPY ["Modules/Tokens/src/Tokens.Infrastructure.Database.SqlServer/Tokens.Infrastructure.Database.SqlServer.csproj", "Modules/Tokens/src/Tokens.Infrastructure.Database.SqlServer/"]
COPY ["Modules/Devices/src/Devices.ConsumerApi/Devices.ConsumerApi.csproj", "Modules/Devices/src/Devices.ConsumerApi/"]
COPY ["Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Devices.Infrastructure.Database.Postgres.csproj", "Modules/Devices/src/Devices.Infrastructure.Database.Postgres/"]
COPY ["Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Devices.Infrastructure.Database.SqlServer.csproj", "Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/"]

RUN dotnet restore /p:ContinuousIntegrationBuild=true "Applications/IdentityDeletionJobs/src/Job.IdentityDeletion/Job.IdentityDeletion.csproj"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<ProjectReference Include="..\..\..\..\BuildingBlocks\src\BuildingBlocks.Infrastructure\BuildingBlocks.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\BuildingBlocks\src\Tooling\Tooling.csproj" />
<ProjectReference Include="..\..\..\..\Infrastructure\Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Announcements\src\Announcements.Application\Announcements.Application.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Announcements\src\Announcements.ConsumerApi\Announcements.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Application\Challenges.Application.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.ConsumerApi\Challenges.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Application\Files.Application.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Backbone.BuildingBlocks.Application.Identities;
using Backbone.BuildingBlocks.Application.QuotaCheck;
using Backbone.Infrastructure.EventBus;
using Backbone.Modules.Announcements.ConsumerApi;
using Backbone.Modules.Challenges.ConsumerApi;
using Backbone.Modules.Devices.ConsumerApi;
using Backbone.Modules.Devices.Infrastructure.PushNotifications;
Expand Down Expand Up @@ -93,6 +94,7 @@ public static IHostBuilder CreateHostBuilder(string[] args)
services.AddTransient(typeof(IHostedService), worker);

services
.AddModule<AnnouncementsModule>(configuration)
.AddModule<DevicesModule>(configuration)
.AddModule<RelationshipsModule>(configuration)
.AddModule<ChallengesModule>(configuration)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Backbone.BuildingBlocks.Application.Identities;

namespace Backbone.Job.IdentityDeletion;

public static class ServicesExtensions
{
public static IServiceCollection RegisterIdentityDeleters(this IServiceCollection services)
{
services.AddTransient<IIdentityDeleter, Modules.Announcements.Application.Announcements.IdentityDeleter>();
services.AddTransient<IIdentityDeleter, Modules.Challenges.Application.Identities.IdentityDeleter>();
services.AddTransient<IIdentityDeleter, Modules.Devices.Application.Identities.IdentityDeleter>();
services.AddTransient<IIdentityDeleter, Modules.Files.Application.Identities.IdentityDeleter>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

var auditLogEntriesForDeletedData = auditLogEntries.Where(e => e.MessageKey == MessageKey.DataDeleted).ToList();

auditLogEntriesForDeletedData.Should().HaveCount(13);
auditLogEntriesForDeletedData.Should().HaveCount(14);

auditLogEntriesForDeletedData.Should().AllSatisfy(e =>
{
Expand All @@ -65,6 +65,7 @@
deletedAggregates.Should().Contain("SyncRuns");
deletedAggregates.Should().Contain("Datawallets");
deletedAggregates.Should().Contain("Tokens");
deletedAggregates.Should().Contain("AnnouncementRecipients");
}

[Fact]
Expand Down Expand Up @@ -126,7 +127,7 @@
var assertionContext = GetService<RelationshipsDbContext>();

var relationshipsAfterAct = await assertionContext.Relationships.Where(Relationship.HasParticipant(identityToBeDeleted.Address)).ToListAsync();
relationshipsAfterAct.Should().BeEmpty();

Check failure on line 130 in Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests.Integration/ActualDeletionWorkerTests.cs

View workflow job for this annotation

GitHub Actions / Run identity-deletion-job Integration Tests (on sqlserver)

Backbone.Job.IdentityDeletion.Tests.Integration.ActualDeletionWorkerTests.Deletes_relationships

Expected relationshipsAfterAct to be empty, but found at least one item { Backbone.Modules.Relationships.Domain.Aggregates.Relationships.Relationship { AuditLog = <null>, CreatedAt = <2025-01-28 13:33:42.503365>, CreationContent = {empty}, CreationResponseContent = {empty}, DomainEvents = {empty}, From = Backbone.DevelopmentKit.Identity.ValueObjects.IdentityAddress { Value = "did:e:prod.enmeshed.eu:dids:53f62d75fa99897131355d" }, FromHasDecomposed = False, Id = Backbone.Modules.Relationships.Domain.Aggregates.Relationships.RelationshipId { Value = "RELETxDDaLzoLxmGOhHn" }, LastModifiedBy = "[Member 'LastModifiedBy' threw an exception: 'Value cannot be null. (Parameter 'source')']", RelationshipTemplate = <null>, RelationshipTemplateId = Backbone.Modules.Relationships.Domain.Aggregates.RelationshipTemplates.RelationshipTemplateId { Value = "RLT8bMWb5dY41Z711W0z" }, Status = RelationshipStatus.Active {value: 20}, To = Backbone.DevelopmentKit.Identity.ValueObjects.IdentityAddress { Value = "did:e:prod.enmeshed.eu:dids:585be965def0bc4477c523" }, ToHasDecomposed = False } }.

Check failure on line 130 in Applications/IdentityDeletionJobs/test/Job.IdentityDeletion.Tests.Integration/ActualDeletionWorkerTests.cs

View workflow job for this annotation

GitHub Actions / Run identity-deletion-job Integration Tests (on postgres)

Backbone.Job.IdentityDeletion.Tests.Integration.ActualDeletionWorkerTests.Deletes_relationships

Expected relationshipsAfterAct to be empty, but found at least one item { Backbone.Modules.Relationships.Domain.Aggregates.Relationships.Relationship { AuditLog = <null>, CreatedAt = <2025-01-28 13:33:21.955291>, CreationContent = {empty}, CreationResponseContent = {empty}, DomainEvents = {empty}, From = Backbone.DevelopmentKit.Identity.ValueObjects.IdentityAddress { Value = "did:e:prod.enmeshed.eu:dids:5d81626889838722eebe3e" }, FromHasDecomposed = False, Id = Backbone.Modules.Relationships.Domain.Aggregates.Relationships.RelationshipId { Value = "RELYjzRPFLCvI9Xkd5he" }, LastModifiedBy = "[Member 'LastModifiedBy' threw an exception: 'Value cannot be null. (Parameter 'source')']", RelationshipTemplate = <null>, RelationshipTemplateId = Backbone.Modules.Relationships.Domain.Aggregates.RelationshipTemplates.RelationshipTemplateId { Value = "RLTcqlfTbeQo302hilFJ" }, Status = RelationshipStatus.Active {value: 20}, To = Backbone.DevelopmentKit.Identity.ValueObjects.IdentityAddress { Value = "did:e:prod.enmeshed.eu:dids:81d1d2a1c6fabacd1916e7" }, ToHasDecomposed = False } }.
}

[Fact]
Expand All @@ -147,6 +148,11 @@
templatesAfterAct.Should().BeEmpty();
}

private T GetService<T>() where T : notnull
{
return _host.Services.CreateScope().ServiceProvider.GetRequiredService<T>();
}

#region Seeders

private async Task<Message> SeedDatabaseWithMessage(Relationship relationship, Identity from, Identity to)
Expand Down Expand Up @@ -216,9 +222,4 @@
}

#endregion

private T GetService<T>() where T : notnull
{
return _host.Services.CreateScope().ServiceProvider.GetRequiredService<T>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public static IRuleBuilderOptions<T, TProperty> In<T, TProperty>(this IRuleBuild
return ruleBuilder
.Must(x => (bool)method.Invoke(null, [x])!)
.WithErrorCode(GenericApplicationErrors.Validation.InvalidPropertyValue().Code)
.WithMessage("The ID is not valid. Check length, prefix and the used characters.");
.WithMessage("'{PropertyName}': The ID or Address is not valid. Check length, prefix and the used characters.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Backbone.Tooling.Extensions;

public static class IEnumerableExtensions
{
public static bool IsEmpty<T>(this IEnumerable<T> items)
{
return !items.Any();
}
}
5 changes: 0 additions & 5 deletions BuildingBlocks/src/Tooling/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,4 @@ public static byte[] GetBytes(this string text)
{
return Encoding.UTF8.GetBytes(text);
}

public static bool IsEmpty<T>(this IEnumerable<T> items)
{
return !items.Any();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public static void ShouldHaveValidationErrorForId<T>(this TestValidationResult<T
{
var errorsForProperty = testValidationResult.ShouldHaveValidationErrorFor(propertyWithInvalidId);
errorsForProperty.Should().Contain(r =>
r.ErrorCode == "error.platform.validation.invalidPropertyValue" && r.ErrorMessage == "The ID is not valid. Check length, prefix and the used characters.");
r.ErrorCode == "error.platform.validation.invalidPropertyValue" && r.ErrorMessage.Contains("The ID or Address is not valid. Check length, prefix and the used characters."));
}

public static void ShouldHaveValidationErrorForIdInCollection<T>(this TestValidationResult<T> testValidationResult, string collectionWithInvalidId, int indexWithInvalidId)
{
var errorsForProperty = testValidationResult.ShouldHaveValidationErrorFor($"{collectionWithInvalidId}[{indexWithInvalidId}]");
errorsForProperty.Should().Contain(r =>
r.ErrorCode == "error.platform.validation.invalidPropertyValue" && r.ErrorMessage == "The ID is not valid. Check length, prefix and the used characters.");
r.ErrorCode == "error.platform.validation.invalidPropertyValue" && r.ErrorMessage.Contains("The ID or Address is not valid. Check length, prefix and the used characters."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class CreateAnnouncementCommand : IRequest<AnnouncementDTO>
public required AnnouncementSeverity Severity { get; set; }
public required List<CreateAnnouncementCommandText> Texts { get; set; }
public DateTime? ExpiresAt { get; set; }

public List<string> Recipients { get; set; } = [];
}

public class CreateAnnouncementCommandText
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Backbone.Modules.Announcements.Application.Announcements.DTOs;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Modules.Announcements.Application.Announcements.DTOs;
using Backbone.Modules.Announcements.Application.Infrastructure.Persistence.Repository;
using Backbone.Modules.Announcements.Domain.Entities;
using MediatR;
Expand All @@ -16,9 +17,11 @@ public Handler(IAnnouncementsRepository announcementsRepository)

public async Task<AnnouncementDTO> Handle(CreateAnnouncementCommand request, CancellationToken cancellationToken)
{
var announcementRecipients = request.Recipients.Select(r => new AnnouncementRecipient(IdentityAddress.Parse(r)));

var texts = request.Texts.Select(t => new AnnouncementText(AnnouncementLanguage.Parse(t.Language), t.Title, t.Body)).ToList();

var announcement = new Announcement(request.Severity, texts, request.ExpiresAt);
var announcement = new Announcement(request.Severity, texts, request.ExpiresAt, announcementRecipients);

await _announcementsRepository.Add(announcement, cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Backbone.BuildingBlocks.Application.Abstractions.Exceptions;
using Backbone.BuildingBlocks.Application.Extensions;
using Backbone.BuildingBlocks.Application.FluentValidation;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Modules.Announcements.Domain.Entities;
using FluentValidation;

Expand All @@ -14,7 +16,12 @@ public Validator()
.WithErrorCode(GenericApplicationErrors.Validation.InvalidPropertyValue().Code)
.WithMessage("There must be a text for English.");

RuleFor(x => x.Recipients.Count)
.LessThanOrEqualTo(100)
.WithErrorCode(GenericApplicationErrors.Validation.InvalidPropertyValue().Code);

RuleForEach(x => x.Texts).SetValidator(new CreateAnnouncementCommandTextValidator());
RuleForEach(x => x.Recipients).ValidId<CreateAnnouncementCommand, IdentityAddress>();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using MediatR;

namespace Backbone.Modules.Announcements.Application.Announcements.Commands.DeleteAnnouncementRecipients;

public record DeleteAnnouncementRecipientsCommand : IRequest
{
public DeleteAnnouncementRecipientsCommand(string identityAddress)
{
IdentityAddress = identityAddress;
}

public string IdentityAddress { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Modules.Announcements.Application.Infrastructure.Persistence.Repository;
using MediatR;

namespace Backbone.Modules.Announcements.Application.Announcements.Commands.DeleteAnnouncementRecipients;

public class Handler : IRequestHandler<DeleteAnnouncementRecipientsCommand>
{
private readonly IAnnouncementsRepository _announcementsRepository;

public Handler(IAnnouncementsRepository announcementsRepository)
{
_announcementsRepository = announcementsRepository;
}

public async Task Handle(DeleteAnnouncementRecipientsCommand request, CancellationToken cancellationToken)
{
var parsedIdentityAddress = IdentityAddress.Parse(request.IdentityAddress);

await _announcementsRepository.DeleteRecipients(r => r.Address == parsedIdentityAddress, cancellationToken);
}
}
Loading
Loading