Skip to content

Commit

Permalink
Merge branch 'main' into migrate-to-flutter-3.29-and-dart-3.7
Browse files Browse the repository at this point in the history
  • Loading branch information
tnotheis authored Feb 27, 2025
2 parents bacddb9 + ba43896 commit 39f83c2
Show file tree
Hide file tree
Showing 87 changed files with 1,800 additions and 304 deletions.
2 changes: 2 additions & 0 deletions .ci/compose.test.postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ services:
command: psql -h postgres -U postgres -d enmeshed -f /app/sql-scripts/setup.sql

admin-cli:
volumes:
- ./appsettings.override.postgres.docker.json:/app/appsettings.override.json
environment:
Database__Provider: Postgres
Database__ConnectionString: "Server=postgres;Database=enmeshed;User Id=devices;Password=Passw0rd;Port=5432"
Expand Down
2 changes: 2 additions & 0 deletions .ci/compose.test.sqlserver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ services:
command: bash -c " sleep 20 && /opt/mssql-tools/bin/sqlcmd -S sqlserver -U SA -P Passw0rd -i /app/sql-scripts/setup.sql"

admin-cli:
volumes:
- ./appsettings.override.sqlserver.docker.json:/app/appsettings.override.json
environment:
Database__Provider: SqlServer
Database__ConnectionString: "Server=sqlserver;Database=enmeshed;User Id=devices;Password=Passw0rd;TrustServerCertificate=True"
Expand Down
4 changes: 2 additions & 2 deletions .run/Admin API.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Admin API" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Applications/AdminApi/src/AdminApi/AdminApi.csproj"/>
<option name="LAUNCH_PROFILE_TFM" value="net8.0" />
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Applications/AdminApi/src/AdminApi/AdminApi.csproj" />
<option name="LAUNCH_PROFILE_TFM" value="net9.0" />
<option name="LAUNCH_PROFILE_NAME" value="Default" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
Expand Down
4 changes: 2 additions & 2 deletions .run/Consumer API.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Consumer API" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Applications/ConsumerApi/src/ConsumerApi.csproj"/>
<option name="LAUNCH_PROFILE_TFM" value="net8.0" />
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Applications/ConsumerApi/src/ConsumerApi.csproj" />
<option name="LAUNCH_PROFILE_TFM" value="net9.0" />
<option name="LAUNCH_PROFILE_NAME" value="Default" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
Expand Down
2 changes: 1 addition & 1 deletion Applications/AdminApi/src/AdminApi/AdminApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.1.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.1.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.24.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="9.0.2" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Backbone.BuildingBlocks.API.Mvc;
using Backbone.BuildingBlocks.API.Mvc.ControllerAttributes;
using Backbone.Modules.Announcements.Application.Announcements.Commands.CreateAnnouncement;
using Backbone.Modules.Announcements.Application.Announcements.DTOs;
using Backbone.Modules.Announcements.Application.Announcements.Queries.GetAllAnnouncements;
using Backbone.Modules.Announcements.Application.Announcements.Queries.GetAnnouncementById;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -11,8 +14,11 @@ namespace Backbone.AdminApi.Controllers;
[Authorize("ApiKey")]
public class AnnouncementsController : ApiControllerBase
{
public AnnouncementsController(IMediator mediator) : base(mediator)
private readonly ILogger<AnnouncementsController> _logger;

public AnnouncementsController(IMediator mediator, ILogger<AnnouncementsController> logger) : base(mediator)
{
_logger = logger;
}

[HttpPost]
Expand All @@ -22,7 +28,17 @@ public async Task<IActionResult> CreateAnnouncement([FromBody] CreateAnnouncemen
return Created(response);
}

[HttpGet("{id}")]
[ProducesResponseType(typeof(AnnouncementDTO), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetAnnouncement(string id, CancellationToken cancellationToken)
{
var response = await _mediator.Send(new GetAnnouncementByIdQuery(id), cancellationToken);
return Ok(response);
}

[HttpGet]
[ProducesResponseType(typeof(GetAllAnnouncementsResponse), StatusCodes.Status200OK)]
public async Task<IActionResult> ListAnnouncements(CancellationToken cancellationToken)
{
var response = await _mediator.Send(new GetAllAnnouncementsQuery(), cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task<IActionResult> GetAllClients(CancellationToken cancellationTok
}

[HttpGet("{id}")]
[ProducesResponseType(typeof(ClientDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<ClientDTO>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetClient([FromRoute] string id, CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public IdentitiesController(IMediator mediator) : base(mediator)
}

[HttpPost("{identityAddress}/Quotas")]
[ProducesResponseType(typeof(IndividualQuotaDTO), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<IndividualQuotaDTO>), StatusCodes.Status201Created)]
[ProducesError(StatusCodes.Status404NotFound)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<CreatedResult> CreateIndividualQuota([FromRoute] string identityAddress, [FromBody] CreateQuotaForIdentityRequest request, CancellationToken cancellationToken)
Expand All @@ -51,7 +51,7 @@ public async Task<IActionResult> DeleteIndividualQuota([FromRoute] string identi
}

[HttpGet("{address}")]
[ProducesResponseType(typeof(GetIdentityResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<GetIdentityResponse>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetIdentityByAddress([FromRoute] string address, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -119,7 +119,7 @@ public async Task<IActionResult> StartDeletionProcess([FromRoute] string address
}

[HttpGet("{identityAddress}/DeletionProcesses")]
[ProducesResponseType(typeof(GetDeletionProcessesAsSupportResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<GetDeletionProcessesAsSupportResponse>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetDeletionProcessesAsSupport([FromRoute] string identityAddress, CancellationToken cancellationToken)
{
Expand All @@ -128,7 +128,7 @@ public async Task<IActionResult> GetDeletionProcessesAsSupport([FromRoute] strin
}

[HttpGet("{identityAddress}/DeletionProcesses/AuditLogs")]
[ProducesResponseType(typeof(GetDeletionProcessesAuditLogsResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<GetDeletionProcessesAuditLogsResponse>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetDeletionProcessesAuditLogs([FromRoute] string identityAddress, CancellationToken cancellationToken)
{
Expand All @@ -146,7 +146,7 @@ public async Task<IActionResult> CancelDeletionProcessAsSupport([FromRoute] stri
}

[HttpGet("{identityAddress}/DeletionProcesses/{deletionProcessId}")]
[ProducesResponseType(typeof(IdentityDeletionProcessDetailsDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<IdentityDeletionProcessDetailsDTO>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetDeletionProcessAsSupport([FromRoute] string identityAddress, [FromRoute] string deletionProcessId, CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task<IActionResult> GetTiers(CancellationToken cancellationToken)
}

[HttpGet("{tierId}")]
[ProducesResponseType(typeof(TierDetailsDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<TierDetailsDTO>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetTierByIdAsync([FromRoute] string tierId, CancellationToken cancellationToken)
{
Expand All @@ -46,7 +46,7 @@ public async Task<IActionResult> GetTierByIdAsync([FromRoute] string tierId, Can
}

[HttpPost]
[ProducesResponseType(typeof(CreateTierResponse), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<CreateTierResponse>), StatusCodes.Status201Created)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<CreatedResult> PostTiers([FromBody] CreateTierCommand command, CancellationToken cancellationToken)
{
Expand All @@ -66,7 +66,7 @@ public async Task<IActionResult> DeleteTier([FromRoute] string tierId, Cancellat
}

[HttpPost("{tierId}/Quotas")]
[ProducesResponseType(typeof(TierQuotaDefinitionDTO), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<TierQuotaDefinitionDTO>), StatusCodes.Status201Created)]
[ProducesError(StatusCodes.Status404NotFound)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<CreatedResult> CreateTierQuota([FromRoute] string tierId, [FromBody] CreateQuotaForTierRequest request, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Backbone.BuildingBlocks.API.Mvc;
using Backbone.BuildingBlocks.API;
using Backbone.BuildingBlocks.API.Mvc;
using Backbone.BuildingBlocks.Application.Abstractions.Exceptions;
using Backbone.BuildingBlocks.Application.Pagination;
using Backbone.Modules.Tokens.Application;
Expand All @@ -17,7 +18,7 @@ namespace Backbone.AdminApi.Controllers;
public class TokensController(IMediator mediator, IOptions<ApplicationOptions> options) : ApiControllerBase(mediator)
{
[HttpGet]
[ProducesResponseType(typeof(List<TokenDTO>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<List<TokenDTO>>), StatusCodes.Status200OK)]
public async Task<IActionResult> ListTokensByIdentity([FromQuery] PaginationFilter paginationFilter, [FromQuery] string createdBy, CancellationToken cancellationToken)
{
if (paginationFilter.PageSize != null)
Expand Down
19 changes: 19 additions & 0 deletions Applications/AdminCli/src/AdminCli/AdminCli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,30 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\Infrastructure\Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Announcements\src\Announcements.Application\Announcements.Application.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Announcements\src\Announcements.Infrastructure\Announcements.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure\Devices.Infrastructure.csproj" />
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="BeforeBuild" Condition="$(Configuration) == Debug">
<Delete Files="$(ProjectDir)appsettings.override.json" />
<Copy SourceFiles="..\..\..\..\appsettings.override.json" DestinationFolder="$(ProjectDir)" UseHardlinksIfPossible="true" />
</Target>

<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

<Content Include="appsettings.override.json" Condition="$(Configuration) == Debug">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.CommandLine;

namespace Backbone.AdminCli.Commands.Announcements;

public class AnnouncementCommand : Command
{
public AnnouncementCommand(SendAnnouncementCommand sendAnnouncementCommand) : base("announcement")
{
AddCommand(sendAnnouncementCommand);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System.CommandLine;
using System.Text.Json;
using Backbone.AdminCli.Commands.BaseClasses;
using Backbone.Modules.Announcements.Application.Announcements.Commands.CreateAnnouncement;
using Backbone.Modules.Announcements.Domain.Entities;
using MediatR;

namespace Backbone.AdminCli.Commands.Announcements;

public class SendAnnouncementCommand : AdminCliCommand
{
public SendAnnouncementCommand(IMediator mediator) : base(mediator, "send")
{
var expiresAt = new Option<string?>("--expiration")
{
IsRequired = false,
Description = "The expiration date of the announcement."
};

var severity = new Option<string?>("--severity")
{
IsRequired = true,
Description = "The severity of the announcement. Possible values: Low, Medium, High"
};

AddOption(expiresAt);
AddOption(severity);

this.SetHandler(SendAnnouncement, severity, expiresAt);
}

private async Task SendAnnouncement(string? severityInput, string? expiresAtInput)
{
try
{
var severity = severityInput switch
{
_ when Enum.TryParse<AnnouncementSeverity>(severityInput, ignoreCase: true, out var parsedSeverity) => parsedSeverity,
_ => throw new ArgumentException($@"Specified severity '{severityInput}' is not a valid severity.")
};

DateTime? expiresAt = expiresAtInput switch
{
_ when string.IsNullOrWhiteSpace(expiresAtInput) => null,
_ when DateTime.TryParse(expiresAtInput, out var parsedDateTime) => parsedDateTime,
_ => throw new ArgumentException($@"Specified expiration datetime '{expiresAtInput}' is not a valid DateTime.")
};

var texts = ReadTextsFromCommandLineInput();

if (texts.Count == 0)
{
Console.WriteLine(@"No texts provided. Exiting...");
return;
}

Console.WriteLine(@"You entered the following texts:");
Console.WriteLine(JsonSerializer.Serialize(texts, JSON_SERIALIZER_OPTIONS));
if (!PromptForConfirmation(@"Do you want to proceed?")) return;

Console.WriteLine(@"Sending announcement...");

try
{
var response = await _mediator.Send(new CreateAnnouncementCommand
{
Texts = texts,
Severity = severity,
ExpiresAt = expiresAt
}, CancellationToken.None);

Console.WriteLine(@"Announcement sent successfully");
Console.WriteLine(JsonSerializer.Serialize(response, JSON_SERIALIZER_OPTIONS));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
catch (Exception e)
{
Console.WriteLine($@"An error occurred: {e.Message}");
}
}

private static List<CreateAnnouncementCommandText> ReadTextsFromCommandLineInput()
{
var texts = new List<CreateAnnouncementCommandText>();

var englishTitle = PromptForInput(@"Enter english title: ");
var englishBody = PromptForInput(@"Enter english body: ");

if (englishTitle == null || englishBody == null) return texts;

texts.Add(new CreateAnnouncementCommandText
{
Language = "en",
Title = englishTitle,
Body = englishBody
});

while (PromptForConfirmation(@"Do you want to add another language?"))
{
var language = PromptForInput(@"Enter a two-letter language code (e.g. de, it, nl): ");
var title = PromptForInput(@"Enter title: ");
var body = PromptForInput(@"Enter body: ");

if (language == null || title == null || body == null)
{
break;
}

texts.Add(new CreateAnnouncementCommandText
{
Language = language,
Title = title,
Body = body
});
}

return texts;
}

private static string? PromptForInput(string prompt, bool allowEmpty = false)
{
Console.Write(prompt);
var input = Console.ReadLine();

while (!allowEmpty && string.IsNullOrWhiteSpace(input))
{
Console.WriteLine(@"Input cannot be empty. Press x to exit.");
Console.Write(prompt);
input = Console.ReadLine();

if (string.IsNullOrWhiteSpace(input))
continue;
if (input.Trim().Equals("x", StringComparison.CurrentCultureIgnoreCase))
return null;

break;
}

return input;
}

private static bool PromptForConfirmation(string prompt)
{
var input = PromptForInput($"{prompt} ([y]es/[N]o): ", true);
return input?.Trim().ToLower() is "yes" or "y";
}
}
Loading

0 comments on commit 39f83c2

Please sign in to comment.