diff --git a/.env.example b/.env.example index b346ea11..04201db1 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,19 @@ # Fully qualified url your venice api used for webhooks and server-side rendering. # Normally this is $SERVER_HOSTNAME/api. e.g. https://connect.example.com/api NEXT_PUBLIC_API_URL="" -POSTGRES_URL="" # Primary database used for metadata and user data storage -JWT_SECRET_OR_PUBLIC_KEY="" # Used for validating authenticity of accessToken -int_plaid__clientId="" # ZodString -int_plaid__secrets__sandbox="" # ZodString -int_plaid__secrets__development="" # ZodString -int_plaid__secrets__production="" # ZodString -int_plaid__clientName="" # ZodString \ No newline at end of file +# Primary database used for metadata and user data storage +POSTGRES_URL="" +# Used for validating authenticity of accessToken +JWT_SECRET_OR_PUBLIC_KEY="" +# +int_plaid__clientId="" +# +int_plaid__secrets__sandbox="" +# +int_plaid__secrets__development="" +# +int_plaid__secrets__production="" +# The name of your application, as it should be displayed in Link. +# Maximum length of 30 characters. +# If a value longer than 30 characters is provided, Link will display "This Application" instead. +int_plaid__clientName="" \ No newline at end of file diff --git a/.github/workflows/validate-workflow.yml b/.github/workflows/validate-workflow.yml index 4238d7ce..c000359b 100644 --- a/.github/workflows/validate-workflow.yml +++ b/.github/workflows/validate-workflow.yml @@ -46,7 +46,7 @@ jobs: run: pnpm run typecheck - name: Run health check - run: node --loader tsx ./bin/ledgerSync health + run: POSTGRES_URL=noop node --loader tsx ./bin/ledgerSync health - name: Generate assets required for lint run: pnpm --dir ./apps/next/ run generate:css diff --git a/apps/app-config/README.md b/apps/app-config/README.md index a1badbbb..5dbfd393 100644 --- a/apps/app-config/README.md +++ b/apps/app-config/README.md @@ -1,14 +1,14 @@ # Environment variables -| Name | Description | -| :------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| NEXT_PUBLIC_API_URL | Fully qualified url your venice api used for webhooks and server-side rendering.
Normally this is $SERVER_HOSTNAME/api. e.g. https://connect.example.com/api | -| POSTGRES_URL | Primary database used for metadata and user data storage | -| JWT_SECRET_OR_PUBLIC_KEY | Used for validating authenticity of accessToken | -| int_plaid__clientId | ZodString | -| int_plaid__secrets__sandbox | ZodString | -| int_plaid__secrets__development | ZodString | -| int_plaid__secrets__production | ZodString | -| int_plaid__clientName | ZodString | +| Name | Description | +| :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `NEXT_PUBLIC_API_URL` | Fully qualified url your venice api used for webhooks and server-side rendering.
Normally this is $SERVER_HOSTNAME/api. e.g. https://connect.example.com/api | +| `POSTGRES_URL` | Primary database used for metadata and user data storage | +| `JWT_SECRET_OR_PUBLIC_KEY` | Used for validating authenticity of accessToken | +| `int_plaid__clientId` | | +| `int_plaid__secrets__sandbox` | | +| `int_plaid__secrets__development` | | +| `int_plaid__secrets__production` | | +| `int_plaid__clientName` | The name of your application, as it should be displayed in Link.
Maximum length of 30 characters.
If a value longer than 30 characters is provided, Link will display "This Application" instead. | diff --git a/apps/app-config/_generateDocs.bin.ts b/apps/app-config/_generateDocs.bin.ts index 91ad24fa..94474b79 100644 --- a/apps/app-config/_generateDocs.bin.ts +++ b/apps/app-config/_generateDocs.bin.ts @@ -3,9 +3,10 @@ import * as path from 'node:path' import tablemark from 'tablemark' -import {compact, R} from '@ledger-sync/util' +import {compact, R, zParser} from '@ledger-sync/util' -import {zAllEnv} from './env' +import {parseIntConfigsFromEnv, zAllEnv} from './env' +import {loadEnv} from './loadEnv.node' const envList = R.pipe( zAllEnv.shape, @@ -19,13 +20,14 @@ const envList = R.pipe( ) return { - Name: key, + Name: '`' + key + '`', Description: cmtLines.join('
'), dotEnvLine: R.pipe(cmtLines.map((c) => `# ${c}`).join('\n'), (cmts) => compact([ - cmtLines.length > 1 && `${cmts}\n`, + `${cmts}\n`, // cmtLines.length > 1 && `${cmts}\n`, `${key}=""`, - cmtLines.length <= 1 && ` ${cmts}`, + // comment on same line is not supported by @next/env due to using old version of dotenv. + // cmtLines.length <= 1 && ` ${cmts}`, ]).join(''), ), } @@ -48,3 +50,12 @@ fs.writeFileSync(dotEnvExampleOutPath, dotEnvExample) console.log(`Wrote ${dotEnvExampleOutPath}`) // console.log(dotEnvExample) + +if (process.env.NODE_ENV !== 'production') { + console.log('Test out loading env vars') + loadEnv() + const env = zParser(zAllEnv).parseUnknown(process.env) + const configs = parseIntConfigsFromEnv(env) + console.log('Parsed env', env) + console.log('Parsed intConfigs', configs) +} diff --git a/apps/app-config/env.ts b/apps/app-config/env.ts index f22d4bf5..fcf44def 100644 --- a/apps/app-config/env.ts +++ b/apps/app-config/env.ts @@ -54,11 +54,6 @@ export const PROVIDERS = [ postgresProvider, ] as const -export const zFlatConfigByProvider = R.mapToObj(DOCUMENTED_PROVIDERS, (p) => [ - p.name, - zFlatten(p.def.integrationConfig ?? z.unknown()), -]) - // MARK: - Env vars export const zCommonEnv = zEnvVars({ @@ -81,14 +76,18 @@ export const zBackendEnv = zEnvVars({ .describe('Used for validating authenticity of accessToken'), }) +export const zFlatConfigByIntId = R.mapToObj(DOCUMENTED_PROVIDERS, (p) => [ + `int_${p.name}`, + zFlatten(p.def.integrationConfig ?? z.unknown()), +]) + export const zIntegrationEnv = zEnvVars( R.pipe( - zFlatConfigByProvider, + zFlatConfigByIntId, R.toPairs, - // R.map((p) => flattenZObject(p.def.integrationConfig, [`int_${p.name}`])), - R.map(([name, schema]) => + R.map(([intId, schema]) => R.mapKeys(schema.innerType().shape, (k) => - compact([`int_${name}`, k]).join('__'), + compact([intId, k]).join('__'), ), ), R.mergeAll, @@ -103,10 +102,10 @@ export function parseIntConfigsFromEnv( env: Record, ) { return R.pipe( - R.mapValues(zFlatConfigByProvider, (zFlatConfig, name) => { + R.mapValues(zFlatConfigByIntId, (zFlatConfig, intId) => { try { const subEnv = R.pipe( - filterObject(env, (key) => key.startsWith(`int_${name}`)), + filterObject(env, (key) => key.startsWith(intId)), R.mapKeys((key) => key.split('__').slice(1).join('__')), (sEnv) => (Object.keys(sEnv).length ? sEnv : undefined), ) @@ -120,7 +119,7 @@ export function parseIntConfigsFromEnv( // const msg = issue.code === 'invalid_type' && issue.message === 'Required' ? `` throw new Error( `Failed to configure "${name}" provider due to invalid env var "${[ - `int_${name}`, + intId, ...issue.path, ].join('__')}": ${issue.message} [${issue.code}]`, ) @@ -130,17 +129,3 @@ export function parseIntConfigsFromEnv( (configMap) => filterObject(configMap, (_, val) => val !== undefined), ) } - -// loadEnv() -// const env = zParser(zAllEnv).parseUnknown(process.env) -// const configs = parseIntConfigs(env) -// console.log(env, configs) -// beancount: undefined, -// onebrick: getEnv('ONEBRICK_CREDENTIALS'), -// alka: { -// baseDir: './data', -// // serviceAccountJson: safeJSONParse( -// // process.env['FIREBASE_SERVICE_ACCOUNT_STAGING'], -// // ), -// // envName: 'staging', -// }, diff --git a/packages/@integrations/integration-plaid/PlaidProvider.ts b/packages/@integrations/integration-plaid/PlaidProvider.ts index 18b31b4f..2148ba17 100644 --- a/packages/@integrations/integration-plaid/PlaidProvider.ts +++ b/packages/@integrations/integration-plaid/PlaidProvider.ts @@ -36,7 +36,14 @@ const _def = makeSyncProvider.def({ name: z.literal('plaid'), // There is a mixing of cases here... Unfortunately... integrationConfig: zPlaidClientConfig.extend({ - clientName: z.string().max(30), + clientName: z + .string() + .max(30) + .describe( + `The name of your application, as it should be displayed in Link. + Maximum length of 30 characters. + If a value longer than 30 characters is provided, Link will display "This Application" instead.`, + ), }), connectionSettings: z.object({ itemId: z.string().nullish(), diff --git a/packages/@utils/util/env-utils.ts b/packages/@utils/util/env-utils.ts index d06de296..72300aec 100644 --- a/packages/@utils/util/env-utils.ts +++ b/packages/@utils/util/env-utils.ts @@ -71,13 +71,13 @@ function flattenZObject( // console.log('schema def', prefixes, schema._def) return { - [prefixes.join('__')]: z + [prefixes.join(separator)]: z .string() .optional() .describe( `${schema.isOptional() ? '[Optional]' : ''} ${ - (schema._def ).typeName ?? '' - } ${schema.description ?? ''}`, + schema.description ?? '' + }`, ), } }