diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.Designer.cs b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.Designer.cs new file mode 100644 index 0000000000..5726294f8d --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.Designer.cs @@ -0,0 +1,106 @@ +// +using System; +using AdminUi.Infrastructure.Persistence.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AdminUi.Infrastructure.Database.Postgres.Migrations +{ + [DbContext(typeof(AdminUiDbContext))] + [Migration("20230929151518_ClientsOverview")] + partial class ClientsOverview + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.ClientOverview", b => + { + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DefaultTier") + .IsRequired() + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("NumberOfIdentities") + .HasColumnType("integer"); + + b.ToTable((string)null); + + b.ToView("ClientOverviews", (string)null); + }); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.IdentityOverview", b => + { + b.Property("Address") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedWithClient") + .HasColumnType("text"); + + b.Property("DatawalletVersion") + .HasColumnType("integer"); + + b.Property("IdentityVersion") + .HasColumnType("smallint"); + + b.Property("LastLoginAt") + .HasColumnType("timestamp with time zone"); + + b.Property("NumberOfDevices") + .HasColumnType("integer"); + + b.Property("TierId") + .IsRequired() + .HasColumnType("text"); + + b.Property("TierName") + .IsRequired() + .HasColumnType("text"); + + b.ToTable((string)null); + + b.ToView("IdentityOverviews", (string)null); + }); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.TierOverview", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NumberOfIdentities") + .HasColumnType("integer"); + + b.ToTable((string)null); + + b.ToView("TierOverviews", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.cs b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.cs new file mode 100644 index 0000000000..a6e4c43409 --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/20230929151518_ClientsOverview.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AdminUi.Infrastructure.Database.Postgres.Migrations +{ + /// + public partial class ClientsOverview : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" + CREATE VIEW "ClientOverviews" AS + SELECT + CLIENTS."ClientId" AS "ClientId", + CLIENTS."DisplayName" AS "DisplayName", + CLIENTS."DefaultTier" AS "DefaultTier", + CLIENTS."CreatedAt" AS "CreatedAt", + ( + SELECT COUNT("ClientId") + FROM "Devices"."Identities" + WHERE "ClientId" = CLIENTS."ClientId" + ) AS "NumberOfIdentities" + FROM "Devices"."OpenIddictApplications" CLIENTS + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" DROP VIEW "ClientOverviews" """); + } + } +} diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/AdminUiDbContextModelSnapshot.cs b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/AdminUiDbContextModelSnapshot.cs index 103a29097d..47e8cf0f12 100644 --- a/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/AdminUiDbContextModelSnapshot.cs +++ b/AdminUi/src/AdminUi.Infrastructure.Database.Postgres/Migrations/AdminUiDbContextModelSnapshot.cs @@ -22,6 +22,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.ClientOverview", b => + { + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DefaultTier") + .IsRequired() + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("NumberOfIdentities") + .HasColumnType("integer"); + + b.ToTable((string)null); + + b.ToView("ClientOverviews", (string)null); + }); + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.IdentityOverview", b => { b.Property("Address") diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.Designer.cs b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.Designer.cs new file mode 100644 index 0000000000..1cc6a75e57 --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.Designer.cs @@ -0,0 +1,106 @@ +// +using System; +using AdminUi.Infrastructure.Persistence.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AdminUi.Infrastructure.Database.SqlServer.Migrations +{ + [DbContext(typeof(AdminUiDbContext))] + [Migration("20230929144733_ClientsOverview")] + partial class ClientsOverview + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.ClientOverview", b => + { + b.Property("ClientId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DefaultTier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfIdentities") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("ClientOverviews", (string)null); + }); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.IdentityOverview", b => + { + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedWithClient") + .HasColumnType("nvarchar(max)"); + + b.Property("DatawalletVersion") + .HasColumnType("int"); + + b.Property("IdentityVersion") + .HasColumnType("tinyint"); + + b.Property("LastLoginAt") + .HasColumnType("datetime2"); + + b.Property("NumberOfDevices") + .HasColumnType("int"); + + b.Property("TierId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TierName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.ToTable((string)null); + + b.ToView("IdentityOverviews", (string)null); + }); + + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.TierOverview", b => + { + b.Property("Id") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfIdentities") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("TierOverviews", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.cs b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.cs new file mode 100644 index 0000000000..79e1f37b47 --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/20230929144733_ClientsOverview.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AdminUi.Infrastructure.Database.SqlServer.Migrations +{ + /// + public partial class ClientsOverview : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" + CREATE VIEW ClientOverviews AS + SELECT + CLIENTS.ClientId, + CLIENTS.DisplayName, + CLIENTS.DefaultTier, + CLIENTS.CreatedAt, + ( + SELECT COUNT(ClientId) + FROM Devices.Identities + WHERE ClientId = CLIENTS.ClientId + ) AS NumberOfIdentities + FROM Devices.OpenIddictApplications CLIENTS + """); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" DROP VIEW ClientOverviews """); + } + } +} diff --git a/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/AdminUiDbContextModelSnapshot.cs b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/AdminUiDbContextModelSnapshot.cs index d3f8db0cef..2b9f621290 100644 --- a/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/AdminUiDbContextModelSnapshot.cs +++ b/AdminUi/src/AdminUi.Infrastructure.Database.SqlServer/Migrations/AdminUiDbContextModelSnapshot.cs @@ -22,6 +22,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.ClientOverview", b => + { + b.Property("ClientId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DefaultTier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfIdentities") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("ClientOverviews", (string)null); + }); + modelBuilder.Entity("AdminUi.Infrastructure.DTOs.IdentityOverview", b => { b.Property("Address") diff --git a/AdminUi/src/AdminUi.Infrastructure/DTOs/ClientOverview.cs b/AdminUi/src/AdminUi.Infrastructure/DTOs/ClientOverview.cs new file mode 100644 index 0000000000..51a66a066e --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure/DTOs/ClientOverview.cs @@ -0,0 +1,9 @@ +namespace AdminUi.Infrastructure.DTOs; +public class ClientOverview +{ + public string ClientId { get; set; } + public string DisplayName { get; set; } + public string DefaultTier { get; set; } + public DateTime CreatedAt { get; set; } + public int NumberOfIdentities { get; set; } +} diff --git a/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/AdminUiDbContext.cs b/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/AdminUiDbContext.cs index a24c97743d..dd3bd2420e 100644 --- a/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/AdminUiDbContext.cs +++ b/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/AdminUiDbContext.cs @@ -22,6 +22,8 @@ public AdminUiDbContext(DbContextOptions options, IServiceProv public DbSet TierOverviews { get; set; } + public DbSet ClientOverviews { get; set; } + protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); diff --git a/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/EntityTypeConfigurations/ClientOverviewEntityTypeConfiguration.cs b/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/EntityTypeConfigurations/ClientOverviewEntityTypeConfiguration.cs new file mode 100644 index 0000000000..5ed176bf05 --- /dev/null +++ b/AdminUi/src/AdminUi.Infrastructure/Persistence/Database/EntityTypeConfigurations/ClientOverviewEntityTypeConfiguration.cs @@ -0,0 +1,13 @@ +using AdminUi.Infrastructure.DTOs; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace AdminUi.Infrastructure.Persistence.Database.EntityTypeConfigurations; +public class ClientOverviewEntityTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToView("ClientOverviews"); + builder.HasNoKey(); + } +} diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts index 1ace609bbd..80a5704eda 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { MatSnackBar } from "@angular/material/snack-bar"; import { ActivatedRoute } from "@angular/router"; -import { Client, ClientDTO, UpdateClientRequest, ClientServiceService } from "src/app/services/client-service/client-service"; +import { Client, UpdateClientRequest, ClientServiceService } from "src/app/services/client-service/client-service"; import { TierOverview, TierService } from "src/app/services/tier-service/tier.service"; import { HttpResponseEnvelope } from "src/app/utils/http-response-envelope"; import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-envelope"; @@ -76,10 +76,10 @@ export class ClientEditComponent { public getClient(): void { this.loading = true; this.clientService.getClientById(this.clientId!).subscribe({ - next: (data: HttpResponseEnvelope) => { + next: (data: HttpResponseEnvelope) => { this.client = { clientId: data.result.clientId, - displayName: data.result.displayName!, + displayName: data.result.displayName, defaultTier: data.result.defaultTier, createdAt: data.result.createdAt } as Client; @@ -149,10 +149,10 @@ export class ClientEditComponent { } as UpdateClientRequest; this.clientService.updateClient(this.client.clientId!, request).subscribe({ - next: (data: HttpResponseEnvelope) => { + next: (data: HttpResponseEnvelope) => { this.client = { clientId: data.result.clientId, - displayName: data.result.displayName!, + displayName: data.result.displayName, defaultTier: data.result.defaultTier, createdAt: data.result.createdAt } as Client; diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.css index 309fbc8f00..95a28b24cf 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.css +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.css @@ -1,11 +1,34 @@ .mat-column-clientId, -.mat-column-displayName, -.mat-column-defaultTier, -.mat-column-createdAt { +.mat-column-displayName { width: 20%; word-break: break-all; } +.mat-column-defaultTier { + width: 15%; + word-break: break-all; +} + +.mat-column-numberOfIdentities { + width: 10%; + word-break: normal; +} + +.mat-column-createdAt { + width: 15%; + word-break: normal; +} + +.mat-column-actions { + width: 20%; + word-break: normal; +} + +.actions-button { + min-height: 36px; + height: auto; +} + .disabled-container { pointer-events: none; opacity: 0.4; diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.html index be112cfe80..c2ea0863d4 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.html @@ -54,6 +54,12 @@

{{ header }}

{{ client.defaultTier }} + + Number of Identities + + {{ client.numberOfIdentities }} + + Created At @@ -63,7 +69,7 @@

{{ header }}

Actions - + diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.ts index f476381251..670f8a69ad 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-list/client-list.component.ts @@ -4,7 +4,7 @@ import { MatSnackBar } from "@angular/material/snack-bar"; import { SelectionModel } from "@angular/cdk/collections"; import { Router } from "@angular/router"; import { MatDialog } from "@angular/material/dialog"; -import { ClientDTO, ClientServiceService } from "src/app/services/client-service/client-service"; +import { ClientOverview, ClientServiceService } from "src/app/services/client-service/client-service"; import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-envelope"; import { forkJoin, Observable } from "rxjs"; import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/confirmation-dialog.component"; @@ -18,13 +18,13 @@ export class ClientListComponent { @ViewChild(MatPaginator) public paginator!: MatPaginator; public header: string; public headerDescription: string; - public clients: ClientDTO[]; + public clients: ClientOverview[]; public totalRecords: number; public pageSize: number; public pageIndex: number; public loading = false; - public selection = new SelectionModel(true, []); - public displayedColumns: string[] = ["select", "clientId", "displayName", "defaultTier", "createdAt", "actions"]; + public selection = new SelectionModel(true, []); + public displayedColumns: string[] = ["select", "clientId", "displayName", "defaultTier", "numberOfIdentities", "createdAt", "actions"]; public constructor( private readonly router: Router, @@ -47,9 +47,9 @@ export class ClientListComponent { public getPagedData(): void { this.loading = true; - this.selection = new SelectionModel(true, []); + this.selection = new SelectionModel(true, []); this.clientService.getClients(this.pageIndex, this.pageSize).subscribe({ - next: (data: PagedHttpResponseEnvelope) => { + next: (data: PagedHttpResponseEnvelope) => { this.clients = data.result; if (data.pagination) { this.totalRecords = data.pagination.totalRecords!; @@ -143,7 +143,7 @@ export class ClientListComponent { this.selection.select(...this.clients); } - public checkboxLabel(index?: number, row?: ClientDTO): string { + public checkboxLabel(index?: number, row?: ClientOverview): string { if (!row || !index) { return `${this.isAllSelected() ? "deselect" : "select"} all`; } diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/client-service/client-service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/client-service/client-service.ts index ef92fd2d59..665b162b65 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/client-service/client-service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/client-service/client-service.ts @@ -14,16 +14,16 @@ export class ClientServiceService { this.apiUrl = `${environment.apiUrl}/Clients`; } - public getClientById(id: string): Observable> { - return this.http.get>(`${this.apiUrl}/${id}`); + public getClientById(id: string): Observable> { + return this.http.get>(`${this.apiUrl}/${id}`); } - public getClients(pageNumber: number, pageSize: number): Observable> { + public getClients(pageNumber: number, pageSize: number): Observable> { const httpOptions = { params: new HttpParams().set("PageNumber", pageNumber + 1).set("PageSize", pageSize) }; - return this.http.get>(this.apiUrl, httpOptions); + return this.http.get>(this.apiUrl, httpOptions); } public createClient(client: Client): Observable> { @@ -38,16 +38,17 @@ export class ClientServiceService { return this.http.patch>(`${this.apiUrl}/${clientId}/ChangeSecret`, request); } - public updateClient(clientId: string, request: UpdateClientRequest): Observable> { - return this.http.patch>(`${this.apiUrl}/${clientId}`, request); + public updateClient(clientId: string, request: UpdateClientRequest): Observable> { + return this.http.patch>(`${this.apiUrl}/${clientId}`, request); } } -export interface ClientDTO { +export interface ClientOverview { clientId: string; displayName?: string; defaultTier: string; createdAt: Date; + numberOfIdentities: number; } export interface Client { diff --git a/AdminUi/src/AdminUi/Controllers/ClientsController.cs b/AdminUi/src/AdminUi/Controllers/ClientsController.cs index 36a444b6cd..e378d64b22 100644 --- a/AdminUi/src/AdminUi/Controllers/ClientsController.cs +++ b/AdminUi/src/AdminUi/Controllers/ClientsController.cs @@ -1,16 +1,18 @@ -using Backbone.Modules.Devices.Application.Clients.Commands.ChangeClientSecret; +using AdminUi.Infrastructure.DTOs; +using AdminUi.Infrastructure.Persistence.Database; +using Backbone.Modules.Devices.Application.Clients.Commands.ChangeClientSecret; using Backbone.Modules.Devices.Application.Clients.Commands.CreateClient; using Backbone.Modules.Devices.Application.Clients.Commands.DeleteClient; using Backbone.Modules.Devices.Application.Clients.Commands.UpdateClient; using Backbone.Modules.Devices.Application.Clients.DTOs; using Backbone.Modules.Devices.Application.Clients.Queries.GetClient; -using Backbone.Modules.Devices.Application.Clients.Queries.ListClients; using Enmeshed.BuildingBlocks.API; using Enmeshed.BuildingBlocks.API.Mvc; using Enmeshed.BuildingBlocks.API.Mvc.ControllerAttributes; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace AdminUi.Controllers; @@ -18,15 +20,20 @@ namespace AdminUi.Controllers; [Authorize("ApiKey")] public class ClientsController : ApiControllerBase { - public ClientsController(IMediator mediator) : base(mediator) { } + private readonly AdminUiDbContext _adminUiDbContext; + + public ClientsController(IMediator mediator, AdminUiDbContext adminUiDbContext) : base(mediator) + { + _adminUiDbContext = adminUiDbContext; + } [HttpGet] - [ProducesResponseType(typeof(HttpResponseEnvelopeResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(HttpResponseEnvelopeResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetAllClients(CancellationToken cancellationToken) { - var clients = await _mediator.Send(new ListClientsQuery(), cancellationToken); - return Ok(clients); + var clientOverviews = await _adminUiDbContext.ClientOverviews.ToListAsync(cancellationToken); + return Ok(clientOverviews); } [HttpGet("{id}")] diff --git a/AdminUi/test/AdminUi.Tests.Integration/API/ClientsApi.cs b/AdminUi/test/AdminUi.Tests.Integration/API/ClientsApi.cs index d93b67fbd6..aa3319626f 100644 --- a/AdminUi/test/AdminUi.Tests.Integration/API/ClientsApi.cs +++ b/AdminUi/test/AdminUi.Tests.Integration/API/ClientsApi.cs @@ -7,9 +7,9 @@ public class ClientsApi : BaseApi { public ClientsApi(IOptions httpConfiguration, HttpClientFactory factory) : base(httpConfiguration, factory) { } - public async Task>> GetAllClients(RequestConfiguration requestConfiguration) + public async Task>> GetAllClients(RequestConfiguration requestConfiguration) { - return await Get>("/Clients", requestConfiguration); + return await Get>("/Clients", requestConfiguration); } public async Task> GetClient(string clientId, RequestConfiguration requestConfiguration) diff --git a/AdminUi/test/AdminUi.Tests.Integration/Features/Clients/GET.feature b/AdminUi/test/AdminUi.Tests.Integration/Features/Clients/GET.feature index 2b7916e7d3..071c48d364 100644 --- a/AdminUi/test/AdminUi.Tests.Integration/Features/Clients/GET.feature +++ b/AdminUi/test/AdminUi.Tests.Integration/Features/Clients/GET.feature @@ -1,9 +1,9 @@ -@Integration +@Integration Feature: GET Clients -User requests a Client List +User requests a Client Overview List -Scenario: Requesting an Client List +Scenario: Requesting a list of existing Clients When a GET request is sent to the /Clients endpoint Then the response status code is 200 (OK) And the response contains a paginated list of Clients diff --git a/AdminUi/test/AdminUi.Tests.Integration/Models/ClientOverviewDTO.cs b/AdminUi/test/AdminUi.Tests.Integration/Models/ClientOverviewDTO.cs new file mode 100644 index 0000000000..e9207ab67a --- /dev/null +++ b/AdminUi/test/AdminUi.Tests.Integration/Models/ClientOverviewDTO.cs @@ -0,0 +1,8 @@ +namespace AdminUi.Tests.Integration.Models; +public class ClientOverviewDTO +{ + public string ClientId { get; set; } + public string DisplayName { get; set; } + public string DefaultTier { get; set; } + public int NumberOfIdentities { get; set; } +} diff --git a/AdminUi/test/AdminUi.Tests.Integration/StepDefinitions/ClientsStepDefinitions.cs b/AdminUi/test/AdminUi.Tests.Integration/StepDefinitions/ClientsStepDefinitions.cs index f084eef76c..7f7bc87502 100644 --- a/AdminUi/test/AdminUi.Tests.Integration/StepDefinitions/ClientsStepDefinitions.cs +++ b/AdminUi/test/AdminUi.Tests.Integration/StepDefinitions/ClientsStepDefinitions.cs @@ -18,7 +18,7 @@ public class ClientsStepDefinitions : BaseStepDefinitions private string _tierId; private string _tier1Id; private string _tier2Id; - private HttpResponse>? _getClientsResponse; + private HttpResponse>? _getClientsResponse; private readonly HttpResponse? _getClientResponse; private readonly HttpResponse? _createClientResponse; private HttpResponse? _changeClientSecretResponse;