Skip to content

Commit

Permalink
feat: Add EU endpoints for seen log (#607)
Browse files Browse the repository at this point in the history
* DB Migration, changed our minds on the record name
* EU endpoint for SeenLog (SO in next PR)
* Rename string hasher class
* Refactoring username/pid, fethcing both name and pid in one function
  • Loading branch information
oskogstad authored Apr 9, 2024
1 parent d216c90 commit 1aa7eeb
Show file tree
Hide file tree
Showing 33 changed files with 2,021 additions and 73 deletions.
184 changes: 176 additions & 8 deletions docs/swagger/V1/swagger.verified.json
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,126 @@
]
}
},
"/api/v1/enduser/dialogs/{dialogId}/seenlog": {
"get": {
"tags": [
"Enduser"
],
"summary": "Gets a single dialog seen log record",
"description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).",
"operationId": "SearchDialogSeenLog",
"parameters": [
{
"name": "dialogId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "guid"
}
}
],
"responses": {
"401": {
"description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"."
},
"403": {
"description": "Forbidden"
},
"200": {
"description": "Successfully returned the dialog seen log record.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchDialogSeenLogDto"
}
}
}
}
},
"404": {
"description": "The given dialog ID or dialog element ID was not found or was already deleted.",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"JWTBearerAuth": []
}
]
}
},
"/api/v1/enduser/dialogs/{dialogId}/seenlog/{seenLogId}": {
"get": {
"tags": [
"Enduser"
],
"summary": "Gets a single dialog seen log record",
"description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).",
"operationId": "GetDialogSeenLog",
"parameters": [
{
"name": "dialogId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "guid"
}
},
{
"name": "seenLogId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "guid"
}
}
],
"responses": {
"401": {
"description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"."
},
"403": {
"description": "Forbidden"
},
"200": {
"description": "Successfully returned the dialog seen log record.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetDialogSeenLogDto"
}
}
}
},
"404": {
"description": "The given dialog ID or dialog element ID was not found or was already deleted.",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"JWTBearerAuth": []
}
]
}
},
"/api/v1/enduser/dialogs": {
"get": {
"tags": [
Expand Down Expand Up @@ -2606,7 +2726,7 @@
"seenSinceLastUpdate": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchDialogDialogSeenRecordDtoSO"
"$ref": "#/components/schemas/SearchDialogDialogSeenLogDtoSO"
}
}
}
Expand All @@ -2626,7 +2746,7 @@
}
}
},
"SearchDialogDialogSeenRecordDtoSO": {
"SearchDialogDialogSeenLogDtoSO": {
"type": "object",
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -2756,7 +2876,7 @@
"seenSinceLastUpdate": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetDialogDialogSeenRecordDtoSO"
"$ref": "#/components/schemas/GetDialogDialogSeenLogDtoSO"
}
}
}
Expand Down Expand Up @@ -2998,7 +3118,7 @@
}
}
},
"GetDialogDialogSeenRecordDtoSO": {
"GetDialogDialogSeenLogDtoSO": {
"type": "object",
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -3450,6 +3570,54 @@
}
}
},
"SearchDialogSeenLogDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"format": "guid"
},
"seenAt": {
"type": "string",
"format": "date-time"
},
"endUserIdHash": {
"type": "string"
},
"endUserName": {
"type": "string",
"nullable": true
},
"isCurrentEndUser": {
"type": "boolean"
}
}
},
"GetDialogSeenLogDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"format": "guid"
},
"seenAt": {
"type": "string",
"format": "date-time"
},
"endUserIdHash": {
"type": "string"
},
"endUserName": {
"type": "string",
"nullable": true
},
"isCurrentEndUser": {
"type": "boolean"
}
}
},
"PaginatedListOfSearchDialogDto": {
"type": "object",
"additionalProperties": false,
Expand Down Expand Up @@ -3536,7 +3704,7 @@
"seenSinceLastUpdate": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchDialogDialogSeenRecordDto"
"$ref": "#/components/schemas/SearchDialogDialogSeenLogDto"
}
}
}
Expand Down Expand Up @@ -3602,7 +3770,7 @@
}
}
},
"SearchDialogDialogSeenRecordDto": {
"SearchDialogDialogSeenLogDto": {
"type": "object",
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -3723,7 +3891,7 @@
"seenSinceLastUpdate": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetDialogDialogSeenRecordDto"
"$ref": "#/components/schemas/GetDialogDialogSeenLogDto"
}
}
}
Expand Down Expand Up @@ -3967,7 +4135,7 @@
}
}
},
"GetDialogDialogSeenRecordDto": {
"GetDialogDialogSeenLogDto": {
"type": "object",
"additionalProperties": false,
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services
.AddScoped<IDialogTokenGenerator, DialogTokenGenerator>()

// Transient
.AddTransient<IStringHasher, RandomSaltStringHasher>()
.AddTransient<IStringHasher, PersistentRandomSaltStringHasher>()
.AddTransient<IUserOrganizationRegistry, UserOrganizationRegistry>()
.AddTransient<IUserResourceRegistry, UserResourceRegistry>()
.AddTransient<IUserNameRegistry, UserNameRegistry>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal interface IStringHasher
string? Hash(string? personIdentifier);
}

internal class RandomSaltStringHasher : IStringHasher
internal class PersistentRandomSaltStringHasher : IStringHasher
{
private const int SaltSize = 16;
private readonly Lazy<byte[]> _lazySalt = new(() => RandomNumberGenerator.GetBytes(SaltSize));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Externals;
Expand All @@ -8,9 +9,11 @@ namespace Digdir.Domain.Dialogporten.Application.Common;
public interface IUserNameRegistry
{
bool TryGetCurrentUserPid([NotNullWhen(true)] out string? userPid);
Task<string?> GetCurrentUserName(string personalIdentificationNumber, CancellationToken cancellationToken);
Task<UserInformation?> GetUserInformation(CancellationToken cancellationToken);
}

public record UserInformation(string UserPid, string? UserName);

public class UserNameRegistry : IUserNameRegistry
{
private readonly IUser _user;
Expand All @@ -24,12 +27,22 @@ public UserNameRegistry(IUser user, INameRegistry nameRegistry)

public bool TryGetCurrentUserPid([NotNullWhen(true)] out string? userPid) => _user.TryGetPid(out userPid);

public async Task<string?> GetCurrentUserName(string personalIdentificationNumber, CancellationToken cancellationToken) =>
await _nameRegistry.GetName(personalIdentificationNumber, cancellationToken);
public async Task<UserInformation?> GetUserInformation(CancellationToken cancellationToken)
{
if (!TryGetCurrentUserPid(out var userPid))
{
return null;
}

var userName = await _nameRegistry.GetName(userPid, cancellationToken);
return new(userPid, userName);
}
}

internal sealed class LocalDevelopmentUserNameRegistryDecorator : IUserNameRegistry
{
private const string LocalDevelopmentUserPid = "Local Development User";

private readonly IUserNameRegistry _userNameRegistry;

public LocalDevelopmentUserNameRegistryDecorator(IUserNameRegistry userNameRegistry)
Expand All @@ -40,6 +53,8 @@ public LocalDevelopmentUserNameRegistryDecorator(IUserNameRegistry userNameRegis
public bool TryGetCurrentUserPid([NotNullWhen(true)] out string? userPid) =>
_userNameRegistry.TryGetCurrentUserPid(out userPid);

public async Task<string?> GetCurrentUserName(string personalIdentificationNumber, CancellationToken cancellationToken)
=> await Task.FromResult("Local Development User");
public Task<UserInformation?> GetUserInformation(CancellationToken cancellationToken)
=> _userNameRegistry.TryGetCurrentUserPid(out var userPid)
? Task.FromResult<UserInformation?>(new UserInformation(userPid!, LocalDevelopmentUserPid))
: throw new UnreachableException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public interface IDialogDbContext

DbSet<OutboxMessage> OutboxMessages { get; }
DbSet<OutboxMessageConsumer> OutboxMessageConsumers { get; }
DbSet<DialogSeenRecord> DialogSeenLog { get; }
DbSet<DialogSeenLog> DialogSeenLog { get; }

/// <summary>
/// Validate a property on the <typeparamref name="TEntity"/> using a lambda
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get;

public class GetDialogSeenLogDto
{
public Guid Id { get; set; }
public DateTimeOffset SeenAt { get; set; }

public string EndUserIdHash { get; set; } = null!;

public string? EndUserName { get; set; }

public bool IsCurrentEndUser { get; set; }
}
Loading

0 comments on commit 1aa7eeb

Please sign in to comment.