Skip to content

Commit

Permalink
Merge branch 'main' into ui-for-sending-announcements
Browse files Browse the repository at this point in the history
  • Loading branch information
tnotheis authored Jan 17, 2025
2 parents f4a3806 + 7366fc4 commit b7125c1
Show file tree
Hide file tree
Showing 173 changed files with 1,230 additions and 678 deletions.
1 change: 1 addition & 0 deletions .ci/appsettings.override.postgres.docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"EventBus": {
"Vendor": "RabbitMQ",
"ConnectionInfo": "rabbitmq",
"RabbitMQEnableSsl": false,
"RabbitMQUsername": "guest",
"RabbitMQPassword": "guest"
},
Expand Down
1 change: 1 addition & 0 deletions .ci/appsettings.override.postgres.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"EventBus": {
"Vendor": "RabbitMQ",
"ConnectionInfo": "localhost",
"RabbitMQEnableSsl": false,
"RabbitMQUsername": "guest",
"RabbitMQPassword": "guest"
},
Expand Down
1 change: 1 addition & 0 deletions .ci/appsettings.override.sqlserver.docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"EventBus": {
"Vendor": "RabbitMQ",
"ConnectionInfo": "rabbitmq",
"RabbitMQEnableSsl": false,
"RabbitMQUsername": "guest",
"RabbitMQPassword": "guest"
},
Expand Down
1 change: 1 addition & 0 deletions .ci/appsettings.override.sqlserver.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"EventBus": {
"Vendor": "RabbitMQ",
"ConnectionInfo": "localhost",
"RabbitMQEnableSsl": false,
"RabbitMQUsername": "guest",
"RabbitMQPassword": "guest"
},
Expand Down
2 changes: 1 addition & 1 deletion .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
// we don't want to update ArchUnitNET currently, because there is a bug in the latest version (see https://github.com/TNG/ArchUnitNET/issues/320)
matchDatasources: ["nuget"],
packageNames: ["TngTech.ArchUnitNET.xUnit"],
packageNames: ["TngTech.ArchUnitNET.xUnit", "FluentAssertions", "FluentAssertions.Json"],
enabled: false
},

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ jobs:
run: |
docker compose -f ./.ci/compose.test.yml -f ./.ci/compose.test.${{matrix.database}}.yml down -v
docker compose -f ./.ci/compose.test.yml -f ./.ci/compose.test.${{matrix.database}}.yml up --no-build --wait -d
docker compose -f ./.ci/compose.test.yml -f ./.ci/compose.test.${{matrix.database}}.yml wait admin-cli
- name: Run integration tests
run: dotnet test --no-restore --no-build --logger "GitHubActions;summary.includeNotFoundTests=false;summary.includeSkippedTests=false;summary.includePassedTests=false" ${{matrix.test-project.path}}
Expand Down
17 changes: 17 additions & 0 deletions Applications/AdminApi/http/Tokens/List Tokens.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
meta {
name: /Tokens
type: http
seq: 1
}

get {
url: {{baseUrl}}/Tokens?createdBy=did:e:localhost:dids:c179861648989f28d189c9&PageNumber=1&PageSize=1
body: none
auth: inherit
}

params:query {
createdBy: did:e:localhost:dids:c179861648989f28d189c9
PageNumber: 1
PageSize: 1
}
12 changes: 7 additions & 5 deletions Applications/AdminApi/src/AdminApi/AdminApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.1.1" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.1.3" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.0.0" />
<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.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="9.0.1" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
Expand All @@ -21,10 +21,10 @@
<PackageReference Include="Serilog.Exceptions.EntityFrameworkCore" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
<!-- CAUTION: Do not upgrade 'AspNetCore.HealthChecks.NpgSql' before the following issue is resolved: https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/1993 -->
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\BuildingBlocks\src\BuildingBlocks.API\BuildingBlocks.API.csproj" />
Expand All @@ -37,6 +37,8 @@
<ProjectReference Include="..\..\..\..\Infrastructure\Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Application\Quotas.Application.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure\Quotas.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Application\Tokens.Application.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure\Tokens.Infrastructure.csproj" />
<ProjectReference Include="..\AdminApi.Infrastructure.Database.Postgres\AdminApi.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\AdminApi.Infrastructure.Database.SqlServer\AdminApi.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\AdminApi.Infrastructure\AdminApi.Infrastructure.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using Backbone.Modules.Tokens.Application;

namespace Backbone.AdminApi.Configuration;

public class TokensConfiguration
{
[Required]
public ApplicationOptions Application { get; set; } = new();

[Required]
public InfrastructureConfiguration Infrastructure { get; set; } = new();

public class InfrastructureConfiguration
{
[Required]
public SqlDatabaseConfiguration SqlDatabase { get; set; } = new();

public class SqlDatabaseConfiguration
{
[Required]
[MinLength(1)]
[RegularExpression("SqlServer|Postgres")]
public string Provider { get; set; } = string.Empty;

[Required]
[MinLength(1)]
public string ConnectionString { get; set; } = string.Empty;

[Required]
public bool EnableHealthCheck { get; set; } = true;
}
}
}
38 changes: 38 additions & 0 deletions Applications/AdminApi/src/AdminApi/Controllers/TokensController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Backbone.BuildingBlocks.API.Mvc;
using Backbone.BuildingBlocks.Application.Abstractions.Exceptions;
using Backbone.BuildingBlocks.Application.Pagination;
using Backbone.Modules.Tokens.Application;
using Backbone.Modules.Tokens.Application.Tokens.DTOs;
using Backbone.Modules.Tokens.Application.Tokens.Queries.ListTokensByIdentity;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using ApplicationException = Backbone.BuildingBlocks.Application.Abstractions.Exceptions.ApplicationException;

namespace Backbone.AdminApi.Controllers;

[Route("api/v1/[controller]")]
[Authorize("ApiKey")]
public class TokensController(IMediator mediator, IOptions<ApplicationOptions> options) : ApiControllerBase(mediator)
{
[HttpGet]
[ProducesResponseType(typeof(List<TokenDTO>), StatusCodes.Status200OK)]
public async Task<IActionResult> ListTokensByIdentity([FromQuery] PaginationFilter paginationFilter, [FromQuery] string createdBy, CancellationToken cancellationToken)
{
if (paginationFilter.PageSize != null)
{
var maxPageSize = options.Value.Pagination.MaxPageSize;

if (paginationFilter.PageSize > maxPageSize)
{
throw new ApplicationException(GenericApplicationErrors.Validation.InvalidPageSize(maxPageSize));
}
}

var request = new ListTokensByIdentityQuery(createdBy, paginationFilter);
var pagedResult = await _mediator.Send(request, cancellationToken);

return Paged(pagedResult);
}
}
14 changes: 9 additions & 5 deletions Applications/AdminApi/src/AdminApi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,22 @@ COPY ["Modules/Announcements/src/Announcements.Application/Announcements.Applica
COPY ["Modules/Announcements/src/Announcements.Domain/Announcements.Domain.csproj", "Modules/Announcements/src/Announcements.Domain/"]
COPY ["Modules/Announcements/src/Announcements.Infrastructure/Announcements.Infrastructure.csproj", "Modules/Announcements/src/Announcements.Infrastructure/"]

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/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.Infrastructure/Challenges.Infrastructure.csproj", "Modules/Challenges/src/Challenges.Infrastructure/"]

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/Quotas/src/Quotas.Application/Quotas.Application.csproj", "Modules/Quotas/src/Quotas.Application/"]
COPY ["Modules/Quotas/src/Quotas.Domain/Quotas.Domain.csproj", "Modules/Quotas/src/Quotas.Domain/"]
COPY ["Modules/Quotas/src/Quotas.Infrastructure/Quotas.Infrastructure.csproj", "Modules/Quotas/src/Quotas.Infrastructure/"]

COPY ["Modules/Tokens/src/Tokens.Application/Tokens.Application.csproj", "Modules/Tokens/src/Tokens.Application/"]
COPY ["Modules/Tokens/src/Tokens.Domain/Tokens.Domain.csproj", "Modules/Tokens/src/Tokens.Domain/"]
COPY ["Modules/Tokens/src/Tokens.Infrastructure/Tokens.Infrastructure.csproj", "Modules/Tokens/src/Tokens.Infrastructure/"]

RUN dotnet restore /p:ContinuousIntegrationBuild=true "Applications/AdminApi/src/AdminApi/AdminApi.csproj"

COPY . .
Expand All @@ -98,7 +102,7 @@ RUN dotnet publish /p:ContinuousIntegrationBuild=true /p:UseAppHost=false --no-r
RUN dotnet publish /p:ContinuousIntegrationBuild=true --configuration Release --output /app/publish/health "/src/Applications/HealthCheck/src/HealthCheck.csproj"

#### Build Flutter Admin UI ####
FROM ghcr.io/cirruslabs/flutter:3.27.1 AS flutter-build-env
FROM ghcr.io/cirruslabs/flutter:3.27.2 AS flutter-build-env

COPY Applications/AdminUi /src
WORKDIR /src
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,7 @@ public static IServiceCollection AddCustomAspNetCore(this IServiceCollection ser
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add(new RedirectAntiforgeryValidationFailedResultFilter());
})
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var firstPropertyWithError =
context.ModelState.First(p => p.Value is { Errors.Count: > 0 });
var nameOfPropertyWithError = firstPropertyWithError.Key;
var firstError = firstPropertyWithError.Value!.Errors.First();
var firstErrorMessage = !string.IsNullOrWhiteSpace(firstError.ErrorMessage)
? firstError.ErrorMessage
: firstError.Exception != null
? firstError.Exception.Message
: "";

var formattedMessage = string.IsNullOrEmpty(nameOfPropertyWithError)
? firstErrorMessage
: $"'{nameOfPropertyWithError}': {firstErrorMessage}";
context.HttpContext.Response.ContentType = "application/json";
var responsePayload = new HttpResponseEnvelopeError(
HttpError.ForProduction(GenericApplicationErrors.Validation.InputCannotBeParsed().Code, formattedMessage,
"")); // TODO: add docs
return new BadRequestObjectResult(responsePayload);
};
})
.ConfigureApiBehaviorOptions(options => options.InvalidModelStateResponseFactory = InvalidModelStateResponseFactory())
.AddJsonOptions(options =>
{
var jsonConverters =
Expand Down Expand Up @@ -151,6 +128,31 @@ public static IServiceCollection AddCustomAspNetCore(this IServiceCollection ser
return services;
}

private static Func<ActionContext, IActionResult> InvalidModelStateResponseFactory() => context =>
{
var (nameOfPropertyWithError, value) = context.ModelState.First(p => p.Value is { Errors.Count: > 0 });

var firstError = value!.Errors.First();
var firstErrorMessage = !string.IsNullOrWhiteSpace(firstError.ErrorMessage)
? firstError.ErrorMessage
: firstError.Exception != null
? firstError.Exception.Message
: "";

var formattedMessage = string.IsNullOrEmpty(nameOfPropertyWithError)
? firstErrorMessage
: $"'{nameOfPropertyWithError}': {firstErrorMessage}";

context.HttpContext.Response.ContentType = "application/json";

var responsePayload = new HttpResponseEnvelopeError(
HttpError.ForProduction(GenericApplicationErrors.Validation.InputCannotBeParsed().Code,
formattedMessage,
"")); // TODO: add docs

return new BadRequestObjectResult(responsePayload);
};

private static object GetPropertyValue(object source, string propertyPath)
{
foreach (var property in propertyPath.Split('.').Select(s => source.GetType().GetProperty(s)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Backbone.AdminApi.Configuration;
using Backbone.Modules.Tokens.Application.Extensions;
using Backbone.Modules.Tokens.Infrastructure.Persistence;
using Microsoft.Extensions.Options;

namespace Backbone.AdminApi.Extensions;

public static class TokensServiceCollectionExtensions
{
public static IServiceCollection AddTokens(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplication(configuration.GetSection("Application"));

services.ConfigureAndValidate<TokensConfiguration.InfrastructureConfiguration>(configuration.GetSection("Infrastructure").Bind);

var infrastructureConfiguration = services.BuildServiceProvider().GetRequiredService<IOptions<TokensConfiguration.InfrastructureConfiguration>>().Value;

services.AddPersistence(options =>
{
options.DbOptions.Provider = infrastructureConfiguration.SqlDatabase.Provider;
options.DbOptions.DbConnectionString = infrastructureConfiguration.SqlDatabase.ConnectionString;
});

return services;
}
}
1 change: 1 addition & 0 deletions Applications/AdminApi/src/AdminApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ static void ConfigureServices(IServiceCollection services, IConfiguration config
.AddCustomIdentity(environment)
.AddDatabase(parsedConfiguration.Infrastructure.SqlDatabase)
.AddDevices(configuration.GetSection("Modules:Devices"))
.AddTokens(configuration.GetSection("Modules:Tokens"))
.AddQuotas(parsedConfiguration.Modules.Quotas)
.AddAnnouncements(parsedConfiguration.Modules.Announcements)
.AddChallenges(parsedConfiguration.Modules.Challenges)
Expand Down
17 changes: 16 additions & 1 deletion Applications/AdminApi/src/AdminApi/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@
"EnableHealthCheck": true
}
}
},

"Tokens": {
"Application": {
"Pagination": {
"DefaultPageSize": 50,
"MaxPageSize": 200
}
},
"Infrastructure": {
"SqlDatabase": {
"EnableHealthCheck": true
}
}
}
},
"Logging": {
Expand All @@ -63,7 +77,6 @@
"Backbone": "Information",
"Enmeshed": "Information",
"AdminApi": "Information",

"Microsoft": "Information"
}
},
Expand All @@ -75,5 +88,7 @@
}
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NJsonSchema.NewtonsoftJson" Version="11.1.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="9.0.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="9.0.1" />
<PackageReference Include="SolidToken.SpecFlow.DependencyInjection" Version="3.9.3" />
<PackageReference Include="SpecFlow.NUnit" Version="3.9.74" />
<PackageReference Include="nunit" Version="4.3.0" />
<PackageReference Include="nunit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@Integration
Feature: GET /Tokens?createdBy={identity-address}

Listing all tokens of an identity that doesn't have any tokens

Scenario: Get all Tokens for an identity with no tokens
Given an identity with no tokens
When a GET request is sent to the /Tokens endpoint with the identity's address
Then the response status code is 200 (OK)
And the response content is an empty array
Loading

0 comments on commit b7125c1

Please sign in to comment.