From 40f424b8fe20da68e1eff0a4507ddc8b953ea892 Mon Sep 17 00:00:00 2001 From: Tyler Myracle Date: Wed, 17 Jan 2024 15:50:05 -0600 Subject: [PATCH] generate mock data for teller --- libs/teller-api/src/types/accounts.ts | 8 +- package.json | 1 + tools/generators/tellerGenerator.ts | 232 ++++++++++++++++++++++++++ workspace.json | 2 +- yarn.lock | 5 + 5 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 tools/generators/tellerGenerator.ts diff --git a/libs/teller-api/src/types/accounts.ts b/libs/teller-api/src/types/accounts.ts index d1ccbcc1..a5628f92 100644 --- a/libs/teller-api/src/types/accounts.ts +++ b/libs/teller-api/src/types/accounts.ts @@ -12,13 +12,15 @@ export enum AccountType { export type DepositorySubtypes = | 'checking' | 'savings' - | 'money market' - | 'certificate of deposit' + | 'money_market' + | 'certificate_of_deposit' | 'treasury' | 'sweep' export type CreditSubtype = 'credit_card' +export type AccountStatus = 'open' | 'closed' + interface BaseAccount { enrollment_id: string links: { @@ -34,7 +36,7 @@ interface BaseAccount { currency: string id: string last_four: string - status: 'open' | 'closed' + status: AccountStatus } interface DepositoryAccount extends BaseAccount { diff --git a/package.json b/package.json index 0ddb5222..c1328b00 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "@babel/core": "7.17.5", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "7.16.7", + "@faker-js/faker": "^8.3.1", "@fast-csv/parse": "^4.3.6", "@next/bundle-analyzer": "^13.1.1", "@nrwl/cli": "15.5.2", diff --git a/tools/generators/tellerGenerator.ts b/tools/generators/tellerGenerator.ts new file mode 100644 index 00000000..a798c2f8 --- /dev/null +++ b/tools/generators/tellerGenerator.ts @@ -0,0 +1,232 @@ +import { faker } from '@faker-js/faker' +import { TellerTypes } from '../../libs/teller-api/src' + +function generateSubType( + type: TellerTypes.AccountTypes +): TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype { + if (type === 'depository') { + return faker.helpers.arrayElement([ + 'checking', + 'savings', + 'money_market', + 'certificate_of_deposit', + 'treasury', + 'sweep', + ]) as TellerTypes.DepositorySubtypes + } else { + return 'credit_card' as TellerTypes.CreditSubtype + } +} + +type GenerateAccountsParams = { + count: number + enrollmentId: string + institutionName: string + institutionId: string +} + +export function generateAccounts({ + count, + enrollmentId, + institutionName, + institutionId, +}: GenerateAccountsParams) { + const accounts: TellerTypes.Account[] = [] + for (let i = 0; i < count; i++) { + const accountId = faker.string.uuid() + const lastFour = faker.finance.creditCardNumber().slice(-4) + const type: TellerTypes.AccountTypes = faker.helpers.arrayElement(['depository', 'credit']) + let subType: TellerTypes.DepositorySubtypes | TellerTypes.CreditSubtype + subType = generateSubType(type) + + const accountStub = { + enrollment_id: enrollmentId, + links: { + balances: `https://api.teller.io/accounts/${accountId}/balances`, + self: `https://api.teller.io/accounts/${accountId}`, + transactions: `https://api.teller.io/accounts/${accountId}/transactions`, + }, + institution: { + name: institutionName, + id: institutionId, + }, + name: faker.finance.accountName(), + currency: 'USD', + id: accountId, + last_four: lastFour, + status: faker.helpers.arrayElement(['open', 'closed']) as TellerTypes.AccountStatus, + } + + if (faker.datatype.boolean()) { + accounts.push({ + ...accountStub, + type: 'depository', + subtype: faker.helpers.arrayElement([ + 'checking', + 'savings', + 'money_market', + 'certificate_of_deposit', + 'treasury', + 'sweep', + ]), + }) + } else { + accounts.push({ + ...accountStub, + type: 'credit', + subtype: 'credit_card', + }) + } + } + return accounts +} + +export function generateBalance(account_id: string): TellerTypes.AccountBalance { + const amount = faker.finance.amount() + return { + available: amount, + ledger: amount, + links: { + account: `https://api.teller.io/accounts/${account_id}`, + self: `https://api.teller.io/accounts/${account_id}/balances`, + }, + account_id, + } +} + +type GenerateAccountsWithBalancesParams = { + count: number + enrollmentId: string + institutionName: string + institutionId: string +} + +export function generateAccountsWithBalances({ + count, + enrollmentId, + institutionName, + institutionId, +}: GenerateAccountsWithBalancesParams): TellerTypes.AccountWithBalances[] { + const accountsWithBalances: TellerTypes.AccountWithBalances[] = [] + for (let i = 0; i < count; i++) { + const account = generateAccounts({ + count, + enrollmentId, + institutionName, + institutionId, + })[0] + const balance = generateBalance(account.id) + accountsWithBalances.push({ + ...account, + balance, + }) + } + return accountsWithBalances +} + +export function generateTransactions(count: number, accountId: string): TellerTypes.Transaction[] { + const transactions: TellerTypes.Transaction[] = [] + + for (let i = 0; i < count; i++) { + const transactionId = `txn_${faker.string.uuid()}` + const transaction = { + details: { + processing_status: faker.helpers.arrayElement(['complete', 'pending']), + category: faker.helpers.arrayElement([ + 'accommodation', + 'advertising', + 'bar', + 'charity', + 'clothing', + 'dining', + 'education', + 'electronics', + 'entertainment', + 'fuel', + 'general', + 'groceries', + 'health', + 'home', + 'income', + 'insurance', + 'investment', + 'loan', + 'office', + 'phone', + 'service', + 'shopping', + 'software', + 'sport', + 'tax', + 'transport', + 'transportation', + 'utilities', + ]), + counterparty: { + name: faker.company.name(), + type: faker.helpers.arrayElement(['person', 'business']), + }, + }, + running_balance: null, + description: faker.word.words({ count: { min: 3, max: 10 } }), + id: transactionId, + date: faker.date.recent({ days: 30 }).toISOString().split('T')[0], // recent date in 'YYYY-MM-DD' format + account_id: accountId, + links: { + account: `https://api.teller.io/accounts/${accountId}`, + self: `https://api.teller.io/accounts/${accountId}/transactions/${transactionId}`, + }, + amount: faker.finance.amount(), + type: faker.helpers.arrayElement(['transfer', 'deposit', 'withdrawal']), + status: faker.helpers.arrayElement(['pending', 'posted']), + } as TellerTypes.Transaction + transactions.push(transaction) + } + return transactions +} + +export function generateEnrollment(): TellerTypes.Enrollment & { institutionId: string } { + const institutionName = faker.company.name() + const institutionId = institutionName.toLowerCase().replace(/\s/g, '_') + return { + accessToken: `token_${faker.string.alphanumeric(15)}`, + user: { + id: `usr_${faker.string.alphanumeric(15)}`, + }, + enrollment: { + id: `enr_${faker.string.alphanumeric(15)}`, + institution: { + name: institutionName, + }, + }, + signatures: [faker.string.alphanumeric(15)], + institutionId, + } +} + +export function generateConnections(count: number) { + const enrollments: (TellerTypes.Enrollment & { institutionId: string })[] = [] + const accountsWithBalances: TellerTypes.AccountWithBalances[] = [] + const transactions: TellerTypes.Transaction[] = [] + for (let i = 0; i < count; i++) { + enrollments.push(generateEnrollment()) + } + enrollments.forEach((enrollment) => { + const accountCount: number = faker.number.int({ min: 1, max: 5 }) + const transactionsCount: number = faker.number.int({ min: 1, max: 50 }) + const enrollmentId = enrollment.enrollment.id + const institutionName = enrollment.enrollment.institution.name + const institutionId = enrollment.institutionId + accountsWithBalances.push( + ...generateAccountsWithBalances({ + count: accountCount, + enrollmentId, + institutionName, + institutionId, + }) + ) + accountsWithBalances.forEach((account) => { + transactions.push(...generateTransactions(transactionsCount, account.id)) + }) + }) +} diff --git a/workspace.json b/workspace.json index 012b52a1..9e263386 100644 --- a/workspace.json +++ b/workspace.json @@ -405,7 +405,7 @@ }, "test": { "executor": "@nrwl/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "outputs": ["/coverage/libs/teller-api"], "options": { "jestConfig": "libs/teller-api/jest.config.ts", "passWithNoTests": true diff --git a/yarn.lock b/yarn.lock index 1fb86738..8121c329 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1730,6 +1730,11 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@faker-js/faker@^8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.3.1.tgz#7753df0cb88d7649becf984a96dd1bd0a26f43e3" + integrity sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw== + "@fast-csv/format@^4.3.5": version "4.3.5" resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3"