From e3a75ccbc6f700329b07ec36f175be9d0f808e43 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 11 Mar 2026 10:51:28 -0500 Subject: [PATCH] feat(api,web): update account and institution handling to use provider identifiers and enhance UI components for account addition --- src/FiscalOS.API/Accounts/Add/Endpoint.cs | 12 +- src/FiscalOS.API/Accounts/Add/Request.cs | 18 +- .../src/components/AddAccountForm.vue | 76 ++++ .../src/components/InstitutionCard.vue | 225 ++++++++++++ .../src/composables/useAccountService.ts | 7 + .../src/composables/useAuthService.ts | 28 +- .../src/composables/useInstitutionService.ts | 28 +- .../src/composables/useService.ts | 33 ++ .../src/composables/useTransactionService.ts | 24 +- src/FiscalOS.Web/src/main.ts | 6 +- .../src/services/accountService.ts | 58 ++++ .../src/services/institutionService.ts | 5 +- .../src/services/transactionService.ts | 11 +- src/FiscalOS.Web/src/views/AccountsView.vue | 324 ++---------------- .../Integration/Accounts/AddTests.cs | 62 ++-- 15 files changed, 484 insertions(+), 433 deletions(-) create mode 100644 src/FiscalOS.Web/src/components/AddAccountForm.vue create mode 100644 src/FiscalOS.Web/src/components/InstitutionCard.vue create mode 100644 src/FiscalOS.Web/src/composables/useAccountService.ts create mode 100644 src/FiscalOS.Web/src/composables/useService.ts create mode 100644 src/FiscalOS.Web/src/services/accountService.ts diff --git a/src/FiscalOS.API/Accounts/Add/Endpoint.cs b/src/FiscalOS.API/Accounts/Add/Endpoint.cs index d376897..779019a 100644 --- a/src/FiscalOS.API/Accounts/Add/Endpoint.cs +++ b/src/FiscalOS.API/Accounts/Add/Endpoint.cs @@ -24,9 +24,9 @@ internal static class Endpoint var userId = httpContext.GetUserId(); var user = await appDbContext.Users - .Include(u => u.Institutions.Where(i => i.Metadata is PlaidInstitutionMetadata && ((PlaidInstitutionMetadata)i.Metadata).PlaidId == request.PlaidInstitutionId)) + .Include(u => u.Institutions.Where(i => i.Metadata is PlaidInstitutionMetadata && ((PlaidInstitutionMetadata)i.Metadata).PlaidId == request.ProviderInstitutionId)) .ThenInclude(i => i.Metadata) - .Include(u => u.Accounts.Where(a => a.Metadata is PlaidAccountMetadata && ((PlaidAccountMetadata)a.Metadata).PlaidId == request.PlaidAccountId)) + .Include(u => u.Accounts.Where(a => a.Metadata is PlaidAccountMetadata && ((PlaidAccountMetadata)a.Metadata).PlaidId == request.ProviderAccountId)) .ThenInclude(a => a.Metadata) .AsSplitQuery() .SingleOrDefaultAsync(u => u.Id == userId, ct); @@ -40,7 +40,7 @@ internal static class Endpoint { return Results.ValidationProblem(new Dictionary { - ["PlaidInstitutionId"] = ["The PlaidInstitutionId field is invalid. No institution connected with the given PlaidInstitutionId was found for the user."], + [nameof(Request.ProviderInstitutionId)] = [$"The {nameof(Request.ProviderInstitutionId)} field is invalid."], }); } @@ -50,7 +50,7 @@ internal static class Endpoint { return Results.ValidationProblem(new Dictionary { - ["PlaidInstitutionId"] = ["The PlaidInstitutionId field is invalid. The connected institution has no plaid metadata"], + [nameof(Request.ProviderInstitutionId)] = [$"The {nameof(Request.ProviderInstitutionId)} field is invalid."], }); } @@ -60,8 +60,8 @@ internal static class Endpoint } var decryptedAccessToken = await encryptor.DecryptAsyncFor(user, plaidInstitutionMetadata.EncryptedAccessToken, ct); - var accountMetadata = PlaidAccountMetadata.From(request.PlaidAccountId, request.PlaidAccountName); - var account = Account.From(request.PlaidAccountName, accountMetadata); + var accountMetadata = PlaidAccountMetadata.From(request.ProviderAccountId, request.ProviderAccountName); + var account = Account.From(request.ProviderAccountName, accountMetadata); user.AddAccount(account); user.Institutions.First().AddAccount(account); diff --git a/src/FiscalOS.API/Accounts/Add/Request.cs b/src/FiscalOS.API/Accounts/Add/Request.cs index e78fb3d..c5a4a13 100644 --- a/src/FiscalOS.API/Accounts/Add/Request.cs +++ b/src/FiscalOS.API/Accounts/Add/Request.cs @@ -2,27 +2,27 @@ namespace FiscalOS.API.Accounts.Add; public record Request : IValidatableObject { - public string PlaidInstitutionId { get; init; } = string.Empty; - public string PlaidAccountId { get; init; } = string.Empty; - public string PlaidAccountName { get; init; } = string.Empty; + public string ProviderInstitutionId { get; init; } = string.Empty; + public string ProviderAccountId { get; init; } = string.Empty; + public string ProviderAccountName { get; init; } = string.Empty; public IEnumerable Validate(ValidationContext validationContext) { - if (string.IsNullOrWhiteSpace(PlaidInstitutionId)) + if (string.IsNullOrWhiteSpace(ProviderInstitutionId)) { - var fieldName = nameof(PlaidInstitutionId); + var fieldName = nameof(ProviderInstitutionId); yield return new($"The {fieldName} field is required.", [fieldName]); } - if (string.IsNullOrWhiteSpace(PlaidAccountId)) + if (string.IsNullOrWhiteSpace(ProviderAccountId)) { - var fieldName = nameof(PlaidAccountId); + var fieldName = nameof(ProviderAccountId); yield return new($"The {fieldName} field is required.", [fieldName]); } - if (string.IsNullOrWhiteSpace(PlaidAccountName)) + if (string.IsNullOrWhiteSpace(ProviderAccountName)) { - var fieldName = nameof(PlaidAccountName); + var fieldName = nameof(ProviderAccountName); yield return new($"The {fieldName} field is required.", [fieldName]); } } diff --git a/src/FiscalOS.Web/src/components/AddAccountForm.vue b/src/FiscalOS.Web/src/components/AddAccountForm.vue new file mode 100644 index 0000000..b6e2d7f --- /dev/null +++ b/src/FiscalOS.Web/src/components/AddAccountForm.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/src/FiscalOS.Web/src/components/InstitutionCard.vue b/src/FiscalOS.Web/src/components/InstitutionCard.vue new file mode 100644 index 0000000..b354022 --- /dev/null +++ b/src/FiscalOS.Web/src/components/InstitutionCard.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/src/FiscalOS.Web/src/composables/useAccountService.ts b/src/FiscalOS.Web/src/composables/useAccountService.ts new file mode 100644 index 0000000..1473d6d --- /dev/null +++ b/src/FiscalOS.Web/src/composables/useAccountService.ts @@ -0,0 +1,7 @@ +import { AccountServiceFactoryKey } from '@/services/accountService'; +import type { UserStore } from '@/stores/userStore'; +import { useService } from './useService'; + +export function useAccountService(store: UserStore) { + return useService(store, AccountServiceFactoryKey); +} diff --git a/src/FiscalOS.Web/src/composables/useAuthService.ts b/src/FiscalOS.Web/src/composables/useAuthService.ts index 488217c..c958b4d 100644 --- a/src/FiscalOS.Web/src/composables/useAuthService.ts +++ b/src/FiscalOS.Web/src/composables/useAuthService.ts @@ -1,27 +1,7 @@ -import { AuthServiceFactoryKey } from "@/services/authService"; -import { ClientConfig, ClientFactoryKey } from "@/services/client"; -import type { UserStore } from "@/stores/userStore"; -import { inject } from "vue"; +import { AuthServiceFactoryKey } from '@/services/authService'; +import type { UserStore } from '@/stores/userStore'; +import { useService } from './useService'; export function useAuthService(store: UserStore) { - const clientFactory = inject(ClientFactoryKey); - const authServiceFactory = inject(AuthServiceFactoryKey); - - if (clientFactory === undefined) { - throw new Error("Failed to inject client factory.") - } - - if (authServiceFactory === undefined) { - throw new Error("Failed to inject auth service factory.") - } - - const clientConfig = new ClientConfig( - { Authorization: `Bearer ${store.user?.token}`}, - true, - store.refreshAccessToken - ); - const client = clientFactory.create(clientConfig); - const authService = authServiceFactory.create(client); - - return authService; + return useService(store, AuthServiceFactoryKey); } diff --git a/src/FiscalOS.Web/src/composables/useInstitutionService.ts b/src/FiscalOS.Web/src/composables/useInstitutionService.ts index 081138c..f7a5469 100644 --- a/src/FiscalOS.Web/src/composables/useInstitutionService.ts +++ b/src/FiscalOS.Web/src/composables/useInstitutionService.ts @@ -1,27 +1,7 @@ -import { ClientConfig, ClientFactoryKey } from "@/services/client"; -import { InstituionServiceFactoryKey } from "@/services/institutionService"; -import type { UserStore } from "@/stores/userStore"; -import { inject } from "vue"; +import { InstitutionServiceFactoryKey } from '@/services/institutionService'; +import type { UserStore } from '@/stores/userStore'; +import { useService } from './useService'; export function useInstitutionService(store: UserStore) { - const clientFactory = inject(ClientFactoryKey); - const institutionServiceFactory = inject(InstituionServiceFactoryKey); - - if (clientFactory === undefined) { - throw new Error("Failed to inject client factory.") - } - - if (institutionServiceFactory === undefined) { - throw new Error("Failed to inject institution service factory.") - } - - const clientConfig = new ClientConfig( - { Authorization: `Bearer ${store.user?.token}`}, - true, - store.refreshAccessToken - ); - const client = clientFactory.create(clientConfig); - const institutionService = institutionServiceFactory.create(client); - - return institutionService; + return useService(store, InstitutionServiceFactoryKey); } diff --git a/src/FiscalOS.Web/src/composables/useService.ts b/src/FiscalOS.Web/src/composables/useService.ts new file mode 100644 index 0000000..5b89466 --- /dev/null +++ b/src/FiscalOS.Web/src/composables/useService.ts @@ -0,0 +1,33 @@ +import type { InjectionKey } from 'vue'; +import { inject } from 'vue'; +import { ClientConfig, ClientFactoryKey, type IClient } from '@/services/client'; +import type { UserStore } from '@/stores/userStore'; + +export interface IServiceFactory { + create: (client: IClient) => TService; +} + +export function useService( + store: UserStore, + serviceFactoryKey: InjectionKey> +): TService { + const clientFactory = inject(ClientFactoryKey); + const serviceFactory = inject(serviceFactoryKey); + + if (clientFactory === undefined) { + throw new Error('Failed to inject client factory.'); + } + + if (serviceFactory === undefined) { + throw new Error('Failed to inject service factory.'); + } + + const clientConfig = new ClientConfig( + { Authorization: `Bearer ${store.user?.token}` }, + true, + store.refreshAccessToken + ); + + const client = clientFactory.create(clientConfig); + return serviceFactory.create(client); +} diff --git a/src/FiscalOS.Web/src/composables/useTransactionService.ts b/src/FiscalOS.Web/src/composables/useTransactionService.ts index 7a50ee8..98aae38 100644 --- a/src/FiscalOS.Web/src/composables/useTransactionService.ts +++ b/src/FiscalOS.Web/src/composables/useTransactionService.ts @@ -1,27 +1,7 @@ -import { ClientConfig, ClientFactoryKey } from '@/services/client'; import { TransactionServiceFactoryKey } from '@/services/transactionService'; import type { UserStore } from '@/stores/userStore'; -import { inject } from 'vue'; +import { useService } from './useService'; export function useTransactionService(store: UserStore) { - const clientFactory = inject(ClientFactoryKey); - const transactionServiceFactory = inject(TransactionServiceFactoryKey); - - if (clientFactory === undefined) { - throw new Error('Failed to inject client factory.'); - } - - if (transactionServiceFactory === undefined) { - throw new Error('Failed to inject transaction service factory.'); - } - - const clientConfig = new ClientConfig( - { Authorization: `Bearer ${store.user?.token}` }, - true, - store.refreshAccessToken - ); - const client = clientFactory.create(clientConfig); - const transactionService = transactionServiceFactory.create(client); - - return transactionService; + return useService(store, TransactionServiceFactoryKey); } diff --git a/src/FiscalOS.Web/src/main.ts b/src/FiscalOS.Web/src/main.ts index ad78699..cb322af 100644 --- a/src/FiscalOS.Web/src/main.ts +++ b/src/FiscalOS.Web/src/main.ts @@ -8,20 +8,22 @@ import router from './router'; import { ClientFactory, ClientFactoryKey } from './services/client'; import { AuthServiceFactory, AuthServiceFactoryKey } from './services/authService'; import { - InstituionServiceFactoryKey, + InstitutionServiceFactoryKey, InstitutionServiceFactory, } from './services/institutionService'; import { TransactionServiceFactory, TransactionServiceFactoryKey, } from './services/transactionService'; +import { AccountServiceFactory, AccountServiceFactoryKey } from './services/accountService'; const app = createApp(App); app.provide(ClientFactoryKey, new ClientFactory()); app.provide(AuthServiceFactoryKey, new AuthServiceFactory()); -app.provide(InstituionServiceFactoryKey, new InstitutionServiceFactory()); +app.provide(InstitutionServiceFactoryKey, new InstitutionServiceFactory()); app.provide(TransactionServiceFactoryKey, new TransactionServiceFactory()); +app.provide(AccountServiceFactoryKey, new AccountServiceFactory()); app.use(createPinia()); app.use(router); diff --git a/src/FiscalOS.Web/src/services/accountService.ts b/src/FiscalOS.Web/src/services/accountService.ts new file mode 100644 index 0000000..efb0c9a --- /dev/null +++ b/src/FiscalOS.Web/src/services/accountService.ts @@ -0,0 +1,58 @@ +import type { InjectionKey } from 'vue'; +import { ClientRequestWithBody, type IClient } from './client'; +import { Err, Ok, type Result } from 'ts-results'; + +type AccountServiceFactoryKeyType = InjectionKey; + +export const AccountServiceFactoryKey: AccountServiceFactoryKeyType = + Symbol('AccountServiceFactory'); + +export interface IAccountServiceFactory { + create: (client: IClient) => IAccountService; +} + +export class AccountServiceFactory implements IAccountServiceFactory { + create(client: IClient): IAccountService { + return new AccountService(client); + } +} + +export interface IAccountService { + add: ( + providerInstitutionId: string, + providerAccountId: string, + providerAccountName: string + ) => Promise>; +} + +export class AccountService implements IAccountService { + private readonly client: IClient; + private readonly endpoints = { + add: '/api/accounts', + }; + + constructor(client: IClient) { + this.client = client; + } + + async add(providerInstitutionId: string, providerAccountId: string, providerAccountName: string) { + const request = new ClientRequestWithBody(this.endpoints.add, undefined, { + providerInstitutionId, + providerAccountId, + providerAccountName, + }); + + try { + const res = await this.client.post(request); + + if (res.ok === false) { + return Err([new Error('Failed to add account.')]); + } + + return Ok(true); + } catch (error) { + console.error(error); + return Err([new Error('Failed to add account.')]); + } + } +} diff --git a/src/FiscalOS.Web/src/services/institutionService.ts b/src/FiscalOS.Web/src/services/institutionService.ts index c53ae3c..e6f8ecb 100644 --- a/src/FiscalOS.Web/src/services/institutionService.ts +++ b/src/FiscalOS.Web/src/services/institutionService.ts @@ -4,8 +4,9 @@ import { ClientRequest, ClientRequestWithBody, type IClient } from './client'; type InstitutionServiceFactoryKeyType = InjectionKey; -export const InstituionServiceFactoryKey: InstitutionServiceFactoryKeyType = - Symbol('AuthServiceFactory'); +export const InstitutionServiceFactoryKey: InstitutionServiceFactoryKeyType = Symbol( + 'InstitutionServiceFactory' +); export interface IInstitutionServiceFactory { create: (client: IClient) => IInstitutionService; diff --git a/src/FiscalOS.Web/src/services/transactionService.ts b/src/FiscalOS.Web/src/services/transactionService.ts index 6b889b1..e2ba0c3 100644 --- a/src/FiscalOS.Web/src/services/transactionService.ts +++ b/src/FiscalOS.Web/src/services/transactionService.ts @@ -4,8 +4,9 @@ import { Err, Ok, type Result } from 'ts-results'; type TransactionServiceFactoryKeyType = InjectionKey; -export const TransactionServiceFactoryKey: TransactionServiceFactoryKeyType = - Symbol('AuthServiceFactory'); +export const TransactionServiceFactoryKey: TransactionServiceFactoryKeyType = Symbol( + 'TransactionServiceFactory' +); export interface ITransactionServiceFactory { create: (client: IClient) => ITransactionService; @@ -40,13 +41,13 @@ export class TransactionService implements ITransactionService { const request = new ClientRequest(url); try { - const response = await this.client.get(request); + const res = await this.client.get(request); - if (response.ok === false) { + if (res.ok === false) { return Err([new Error('Failed to retrieve transactions.')]); } - const data = await response.json(); + const data = await res.json(); return Ok(data as Page); } catch (error) { console.error(error); diff --git a/src/FiscalOS.Web/src/views/AccountsView.vue b/src/FiscalOS.Web/src/views/AccountsView.vue index dca2dfd..3c40a32 100644 --- a/src/FiscalOS.Web/src/views/AccountsView.vue +++ b/src/FiscalOS.Web/src/views/AccountsView.vue @@ -1,7 +1,7 @@ @@ -259,147 +120,4 @@ gap: 1rem; padding: 1rem; } - - .institution-container { - display: flex; - flex-direction: column; - gap: 0.5rem; - } - - .institution-card, - .account-card, - .add-account-card { - display: flex; - align-items: center; - padding: 1rem; - border-radius: 0.25rem; - background: var(--bg-surface); - } - - .institution-card, - .account-card { - position: relative; - } - - .institution-card > div { - flex: 1; - } - - .institution-card > div:last-of-type { - display: flex; - justify-content: flex-end; - } - - .institution-card::before { - content: ''; - position: absolute; - top: 50%; - left: -0.25rem; - transform: translateY(-50%); - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; - background: var(--brand-primary); - z-index: 1; - } - - .institution-card::after { - content: ''; - position: absolute; - top: 50%; - left: -1px; - height: calc(50% + 0.5rem); - width: 2px; - background: var(--brand-primary); - } - - .add-account-card { - margin-left: 2rem; - } - - .add-account-card { - gap: 0.5rem; - } - - .accounts-list { - display: flex; - flex-direction: column; - gap: 0.5rem; - } - - .account-row { - position: relative; - padding-left: 2rem; - } - - .account-row::before { - content: ''; - position: absolute; - top: 0; - left: -1px; - height: calc(100% + 0.5rem); - width: 2px; - background: var(--brand-primary); - } - - .account-row:not(:first-child)::before { - top: -0.5rem; - height: calc(100% + 1rem); - } - - .account-row:last-child::before { - height: 50%; - } - - .account-row:not(:first-child):last-child::before { - top: -0.5rem; - height: calc(50% + 0.5rem); - } - - .account-row::after { - content: ''; - position: absolute; - top: 50%; - left: -1px; - width: calc(2rem + 1px); - height: 2px; - background: var(--brand-primary); - transform: translateY(-50%); - } - - .account-card::before { - content: ''; - position: absolute; - top: 50%; - left: -0.25rem; - transform: translateY(-50%); - width: 0.5rem; - height: 0.5rem; - border-radius: 50%; - background: var(--brand-primary); - z-index: 1; - } - - .select-account-container { - display: flex; - gap: 0.25rem; - align-items: center; - } - - .select-account-container > select { - padding: 0.25rem; - border-radius: 0.25rem; - } - - .add-institution-button, - .add-account-button { - background: var(--bg-element); - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - } - - .add-account-button:disabled { - cursor: not-allowed; - opacity: 0.6; - } diff --git a/tests/FiscalOS.API.Tests/Integration/Accounts/AddTests.cs b/tests/FiscalOS.API.Tests/Integration/Accounts/AddTests.cs index b9297db..5a291b9 100644 --- a/tests/FiscalOS.API.Tests/Integration/Accounts/AddTests.cs +++ b/tests/FiscalOS.API.Tests/Integration/Accounts/AddTests.cs @@ -1,3 +1,4 @@ +using FiscalOS.API.Accounts.Add; using FiscalOS.Core.Queuing; using FiscalOS.Infra.Transactions.Plaid; @@ -31,9 +32,9 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) await response.Should().BeValidationProblemDetails(new Dictionary() { - ["PlaidInstitutionId"] = ["The PlaidInstitutionId field is required."], - ["PlaidAccountId"] = ["The PlaidAccountId field is required."], - ["PlaidAccountName"] = ["The PlaidAccountName field is required."], + [nameof(Request.ProviderInstitutionId)] = [$"The {nameof(Request.ProviderInstitutionId)} field is required."], + [nameof(Request.ProviderAccountId)] = [$"The {nameof(Request.ProviderAccountId)} field is required."], + [nameof(Request.ProviderAccountName)] = [$"The {nameof(Request.ProviderAccountName)} field is required."], }); } @@ -45,9 +46,8 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(Guid.NewGuid()) .WithBody(new { - plaidAccountId = "accountId", - plaidAccountName = "Some Account", - accountCurrencyCode = "USD", + providerAccountId = "accountId", + providerAccountName = "Some Account", }) .Build(); @@ -55,7 +55,7 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) await response.Should().BeValidationProblemDetails(new Dictionary() { - ["PlaidInstitutionId"] = ["The PlaidInstitutionId field is required."], + [nameof(Request.ProviderInstitutionId)] = [$"The {nameof(Request.ProviderInstitutionId)} field is required."], }); } @@ -67,9 +67,8 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(Guid.NewGuid()) .WithBody(new { - plaidInstitutionId = "institutionId", - plaidAccountName = "Some Account", - accountCurrencyCode = "USD", + providerInstitutionId = "institutionId", + providerAccountName = "Some Account", }) .Build(); @@ -77,7 +76,7 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) await response.Should().BeValidationProblemDetails(new Dictionary() { - ["PlaidAccountId"] = ["The PlaidAccountId field is required."], + [nameof(Request.ProviderAccountId)] = [$"The {nameof(Request.ProviderAccountId)} field is required."], }); } @@ -89,9 +88,8 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(Guid.NewGuid()) .WithBody(new { - plaidInstitutionId = "institutionId", - plaidAccountId = "accountId", - accountCurrencyCode = "USD", + providerInstitutionId = "institutionId", + providerAccountId = "accountId", }) .Build(); @@ -99,7 +97,7 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) await response.Should().BeValidationProblemDetails(new Dictionary() { - ["PlaidAccountName"] = ["The PlaidAccountName field is required."], + [nameof(Request.ProviderAccountName)] = [$"The {nameof(Request.ProviderAccountName)} field is required."], }); } @@ -111,10 +109,9 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(Guid.NewGuid()) .WithBody(new { - plaidInstitutionId = "id", - plaidAccountId = "id", - plaidAccountName = "Some Account", - accountCurrencyCode = "USD", + providerInstitutionId = "id", + providerAccountId = "id", + providerAccountName = "Some Account", }) .Build(); @@ -145,10 +142,9 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(user.Id) .WithBody(new { - plaidInstitutionId = "id", - plaidAccountId = "id", - plaidAccountName = "Some Account", - accountCurrencyCode = "USD", + providerInstitutionId = "id", + providerAccountId = "id", + providerAccountName = "Some Account", }) .Build(); @@ -156,7 +152,7 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) await response.Should().BeValidationProblemDetails(new Dictionary() { - ["PlaidInstitutionId"] = ["The PlaidInstitutionId field is invalid. No institution connected with the given PlaidInstitutionId was found for the user."], + [nameof(Request.ProviderInstitutionId)] = [$"The {nameof(Request.ProviderInstitutionId)} field is invalid."], }); } @@ -199,10 +195,9 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) .WithUserId(user.Id) .WithBody(new { - plaidInstitutionId = ((PlaidInstitutionMetadata)institution.Metadata!).PlaidId, - plaidAccountId = ((PlaidAccountMetadata)account.Metadata!).PlaidId, - plaidAccountName = ((PlaidAccountMetadata)account.Metadata).PlaidName, - accountCurrencyCode = "USD", + providerInstitutionId = ((PlaidInstitutionMetadata)institution.Metadata!).PlaidId, + providerAccountId = ((PlaidAccountMetadata)account.Metadata!).PlaidId, + providerAccountName = ((PlaidAccountMetadata)account.Metadata).PlaidName, }) .Build(); @@ -252,20 +247,15 @@ public class AddTests(TestApi testApi) : IntegrationTest(testApi) var newAccountId = "newAccountId"; var newAccountName = "New Account"; - var expectedBalance = 100; - var expectedCurrencyCode = "USD"; using var request = HttpRequestBuilder.New() .Post(AddUri) .WithUserId(user.Id) .WithBody(new { - plaidInstitutionId = ((PlaidInstitutionMetadata)institution.Metadata!).PlaidId, - plaidAccountId = newAccountId, - plaidAccountName = newAccountName, - accountCurrentBalance = expectedBalance, - accountAvailableBalance = expectedBalance, - accountCurrencyCode = expectedCurrencyCode, + providerInstitutionId = ((PlaidInstitutionMetadata)institution.Metadata!).PlaidId, + providerAccountId = newAccountId, + providerAccountName = newAccountName, }) .Build();