refactor(api): introduce DTOs for institutional account discovery

- Replace raw Plaid entity response with a dedicated Response wrapper
- Map Plaid accounts to AccountDto to decouple API from external SDK
models
- Update integration tests to assert against the new response structure
- Add JsonConstructors to DTO records for reliable deserialization
This commit is contained in:
Stevan Freeborn
2026-02-14 06:46:05 -06:00
parent dad2ceb450
commit 3b7830b1bc
3 changed files with 45 additions and 3 deletions
@@ -44,7 +44,8 @@ internal static class Endpoint
var accessToken = await encryptor.DecryptAsyncFor(user, plaidMetadata.EncryptedAccessToken, ct);
var accounts = await plaidService.GetAccountsAsync(accessToken);
var accountsDtos = accounts.Select(AccountDto.FromPlaidAccount);
return Results.Ok(accounts);
return Results.Ok(Response.From(accountsDtos));
}
}
@@ -0,0 +1,39 @@
namespace FiscalOS.API.Institutions.GetAvailable;
internal sealed record Response
{
public IEnumerable<AccountDto> Accounts { get; init; } = [];
[JsonConstructor]
private Response()
{
}
public static Response From(IEnumerable<AccountDto> accounts)
{
return new Response
{
Accounts = accounts
};
}
}
internal sealed record AccountDto
{
public string ProviderId { get; init; } = string.Empty;
public string ProviderName { get; init; } = string.Empty;
[JsonConstructor]
private AccountDto()
{
}
public static AccountDto FromPlaidAccount(Going.Plaid.Entity.Account plaidAccount)
{
return new AccountDto
{
ProviderId = plaidAccount.AccountId,
ProviderName = plaidAccount.Name
};
}
}
@@ -1,3 +1,5 @@
using FiscalOS.API.Institutions.GetAvailable;
using Institution = FiscalOS.Core.Accounts.Institution;
namespace FiscalOS.API.Tests.Integration.Institutions;
@@ -108,7 +110,7 @@ public class GetAvailableTests(TestApi testApi) : IntegrationTest(testApi)
var response = await Client.SendAsync(request, TestContext.Current.CancellationToken);
(await response.Should().BeJsonContentOfType<List<Account>>(HttpStatusCode.OK))
.Which.Should().BeEquivalentTo(expectedAccounts);
(await response.Should().BeJsonContentOfType<Response>(HttpStatusCode.OK))
.Which.Accounts.Should().BeEquivalentTo(expectedAccounts.Select(AccountDto.FromPlaidAccount));
}
}