diff --git a/.gitignore b/.gitignore index 89be0ad5..0559b409 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ # next.js /apps/web/.next/ /apps/web/out/ +.worker-next/ +.dev.vars # production /apps/web/build @@ -64,6 +66,8 @@ dist/ /gateway/config.json /gateway/wrangler.toml +# Cloudflare +.wrangler .dev.vars # CI Artifacts @@ -71,3 +75,5 @@ dist/ # Next.js .next/ + +wrangler.toml \ No newline at end of file diff --git a/apps/app-config/connectors/connectors.def.ts b/apps/app-config/connectors/connectors.def.ts index cc622d02..de3dca68 100644 --- a/apps/app-config/connectors/connectors.def.ts +++ b/apps/app-config/connectors/connectors.def.ts @@ -1,6 +1,4 @@ // generated by _generateConnectorLists.ts. Do not modify by hand -import {default as connectorAircall} from '@openint/connector-aircall/def' -import {default as connectorAirtable} from '@openint/connector-airtable/def' import {default as connectorApollo} from '@openint/connector-apollo/def' import {default as connectorBeancount} from '@openint/connector-beancount/def' import {default as connectorBrex} from '@openint/connector-brex/def' @@ -9,8 +7,6 @@ import {default as connectorConfluence} from '@openint/connector-confluence/def' import {default as connectorDebug} from '@openint/connector-debug/def' import {default as connectorDiscord} from '@openint/connector-discord/def' import {default as connectorFinch} from '@openint/connector-finch/def' -import {default as connectorFirebase} from '@openint/connector-firebase/def' -import {default as connectorForeceipt} from '@openint/connector-foreceipt/def' import {default as connectorFs} from '@openint/connector-fs/def' import {default as connectorGithub} from '@openint/connector-github/def' import {default as connectorGong} from '@openint/connector-gong/def' @@ -27,7 +23,6 @@ import {default as connectorLunchmoney} from '@openint/connector-lunchmoney/def' import {default as connectorMercury} from '@openint/connector-mercury/def' import {default as connectorMerge} from '@openint/connector-merge/def' import {default as connectorMicrosoft} from '@openint/connector-microsoft/def' -import {default as connectorMongodb} from '@openint/connector-mongodb/def' import {default as connectorMoota} from '@openint/connector-moota/def' import {default as connectorOnebrick} from '@openint/connector-onebrick/def' import {default as connectorOutreach} from '@openint/connector-outreach/def' @@ -55,8 +50,6 @@ import {default as connectorYodlee} from '@openint/connector-yodlee/def' import {default as connectorZohodesk} from '@openint/connector-zohodesk/def' export const defConnectors = { - aircall: connectorAircall, - airtable: connectorAirtable, apollo: connectorApollo, beancount: connectorBeancount, brex: connectorBrex, @@ -65,8 +58,6 @@ export const defConnectors = { debug: connectorDebug, discord: connectorDiscord, finch: connectorFinch, - firebase: connectorFirebase, - foreceipt: connectorForeceipt, fs: connectorFs, github: connectorGithub, gong: connectorGong, @@ -83,7 +74,6 @@ export const defConnectors = { mercury: connectorMercury, merge: connectorMerge, microsoft: connectorMicrosoft, - mongodb: connectorMongodb, moota: connectorMoota, onebrick: connectorOnebrick, outreach: connectorOutreach, diff --git a/apps/app-config/connectors/connectors.merged.ts b/apps/app-config/connectors/connectors.merged.ts index c4c1d31c..018f4b08 100644 --- a/apps/app-config/connectors/connectors.merged.ts +++ b/apps/app-config/connectors/connectors.merged.ts @@ -1,8 +1,4 @@ // generated by _generateConnectorLists.ts. Do not modify by hand -import {default as connectorAircall_def} from '@openint/connector-aircall/def' -import {default as connectorAircall_server} from '@openint/connector-aircall/server' -import {default as connectorAirtable_def} from '@openint/connector-airtable/def' -import {default as connectorAirtable_server} from '@openint/connector-airtable/server' import {default as connectorApollo_def} from '@openint/connector-apollo/def' import {default as connectorApollo_server} from '@openint/connector-apollo/server' import {default as connectorBeancount_def} from '@openint/connector-beancount/def' @@ -19,10 +15,6 @@ import {default as connectorDiscord_def} from '@openint/connector-discord/def' import {default as connectorDiscord_server} from '@openint/connector-discord/server' import {default as connectorFinch_def} from '@openint/connector-finch/def' import {default as connectorFinch_server} from '@openint/connector-finch/server' -import {default as connectorFirebase_def} from '@openint/connector-firebase/def' -import {default as connectorFirebase_server} from '@openint/connector-firebase/server' -import {default as connectorForeceipt_def} from '@openint/connector-foreceipt/def' -import {default as connectorForeceipt_server} from '@openint/connector-foreceipt/server' import {default as connectorFs_def} from '@openint/connector-fs/def' import {default as connectorFs_server} from '@openint/connector-fs/server' import {default as connectorGithub_def} from '@openint/connector-github/def' @@ -54,8 +46,6 @@ import {default as connectorMerge_def} from '@openint/connector-merge/def' import {default as connectorMerge_server} from '@openint/connector-merge/server' import {default as connectorMicrosoft_def} from '@openint/connector-microsoft/def' import {default as connectorMicrosoft_server} from '@openint/connector-microsoft/server' -import {default as connectorMongodb_def} from '@openint/connector-mongodb/def' -import {default as connectorMongodb_server} from '@openint/connector-mongodb/server' import {default as connectorMoota_def} from '@openint/connector-moota/def' import {default as connectorMoota_server} from '@openint/connector-moota/server' import {default as connectorOnebrick_def} from '@openint/connector-onebrick/def' @@ -106,16 +96,6 @@ import {default as connectorYodlee_server} from '@openint/connector-yodlee/serve import {default as connectorZohodesk_def} from '@openint/connector-zohodesk/def' import {default as connectorZohodesk_server} from '@openint/connector-zohodesk/server' -const connectorAircall = { - ...connectorAircall_def, - ...connectorAircall_server, -} - -const connectorAirtable = { - ...connectorAirtable_def, - ...connectorAirtable_server, -} - const connectorApollo = { ...connectorApollo_def, ...connectorApollo_server, @@ -156,16 +136,6 @@ const connectorFinch = { ...connectorFinch_server, } -const connectorFirebase = { - ...connectorFirebase_def, - ...connectorFirebase_server, -} - -const connectorForeceipt = { - ...connectorForeceipt_def, - ...connectorForeceipt_server, -} - const connectorFs = { ...connectorFs_def, ...connectorFs_server, @@ -245,11 +215,6 @@ const connectorMicrosoft = { ...connectorMicrosoft_server, } -const connectorMongodb = { - ...connectorMongodb_def, - ...connectorMongodb_server, -} - const connectorMoota = { ...connectorMoota_def, ...connectorMoota_server, @@ -375,8 +340,6 @@ const connectorZohodesk = { } export const mergedConnectors = { - aircall: connectorAircall, - airtable: connectorAirtable, apollo: connectorApollo, beancount: connectorBeancount, brex: connectorBrex, @@ -385,8 +348,6 @@ export const mergedConnectors = { debug: connectorDebug, discord: connectorDiscord, finch: connectorFinch, - firebase: connectorFirebase, - foreceipt: connectorForeceipt, fs: connectorFs, github: connectorGithub, gong: connectorGong, @@ -403,7 +364,6 @@ export const mergedConnectors = { mercury: connectorMercury, merge: connectorMerge, microsoft: connectorMicrosoft, - mongodb: connectorMongodb, moota: connectorMoota, onebrick: connectorOnebrick, outreach: connectorOutreach, diff --git a/apps/app-config/connectors/connectors.server.ts b/apps/app-config/connectors/connectors.server.ts index b00e315e..ae1ecc34 100644 --- a/apps/app-config/connectors/connectors.server.ts +++ b/apps/app-config/connectors/connectors.server.ts @@ -1,6 +1,4 @@ // generated by _generateConnectorLists.ts. Do not modify by hand -import {default as connectorAircall} from '@openint/connector-aircall/server' -import {default as connectorAirtable} from '@openint/connector-airtable/server' import {default as connectorApollo} from '@openint/connector-apollo/server' import {default as connectorBeancount} from '@openint/connector-beancount/server' import {default as connectorBrex} from '@openint/connector-brex/server' @@ -9,8 +7,6 @@ import {default as connectorConfluence} from '@openint/connector-confluence/serv import {default as connectorDebug} from '@openint/connector-debug/server' import {default as connectorDiscord} from '@openint/connector-discord/server' import {default as connectorFinch} from '@openint/connector-finch/server' -import {default as connectorFirebase} from '@openint/connector-firebase/server' -import {default as connectorForeceipt} from '@openint/connector-foreceipt/server' import {default as connectorFs} from '@openint/connector-fs/server' import {default as connectorGithub} from '@openint/connector-github/server' import {default as connectorGong} from '@openint/connector-gong/server' @@ -26,7 +22,6 @@ import {default as connectorLinear} from '@openint/connector-linear/server' import {default as connectorLunchmoney} from '@openint/connector-lunchmoney/server' import {default as connectorMerge} from '@openint/connector-merge/server' import {default as connectorMicrosoft} from '@openint/connector-microsoft/server' -import {default as connectorMongodb} from '@openint/connector-mongodb/server' import {default as connectorMoota} from '@openint/connector-moota/server' import {default as connectorOnebrick} from '@openint/connector-onebrick/server' import {default as connectorOutreach} from '@openint/connector-outreach/server' @@ -53,8 +48,6 @@ import {default as connectorYodlee} from '@openint/connector-yodlee/server' import {default as connectorZohodesk} from '@openint/connector-zohodesk/server' export const serverConnectors = { - aircall: connectorAircall, - airtable: connectorAirtable, apollo: connectorApollo, beancount: connectorBeancount, brex: connectorBrex, @@ -63,8 +56,6 @@ export const serverConnectors = { debug: connectorDebug, discord: connectorDiscord, finch: connectorFinch, - firebase: connectorFirebase, - foreceipt: connectorForeceipt, fs: connectorFs, github: connectorGithub, gong: connectorGong, @@ -80,7 +71,6 @@ export const serverConnectors = { lunchmoney: connectorLunchmoney, merge: connectorMerge, microsoft: connectorMicrosoft, - mongodb: connectorMongodb, moota: connectorMoota, onebrick: connectorOnebrick, outreach: connectorOutreach, diff --git a/apps/app-config/connectors/meta.js b/apps/app-config/connectors/meta.js index fbd20c99..ff7429b5 100644 --- a/apps/app-config/connectors/meta.js +++ b/apps/app-config/connectors/meta.js @@ -10,15 +10,6 @@ module.exports = [ server: '@openint/connector-aircall/server', }, }, - { - name: 'airtable', - dirName: 'connector-airtable', - varName: 'connectorAirtable', - imports: { - def: '@openint/connector-airtable/def', - server: '@openint/connector-airtable/server', - }, - }, { dirName: 'connector-alphavantage', varName: 'connectorAlphavantage', @@ -60,16 +51,6 @@ module.exports = [ server: '@openint/connector-coda/server', }, }, - { - name: 'confluence', - dirName: 'connector-confluence', - varName: 'connectorConfluence', - imports: { - def: '@openint/connector-confluence/def', - server: '@openint/connector-confluence/server', - }, - }, - {dirName: 'connector-copilot', varName: 'connectorCopilot', imports: {}}, { name: 'debug', dirName: 'connector-debug', @@ -99,24 +80,6 @@ module.exports = [ server: '@openint/connector-finch/server', }, }, - { - name: 'firebase', - dirName: 'connector-firebase', - varName: 'connectorFirebase', - imports: { - def: '@openint/connector-firebase/def', - server: '@openint/connector-firebase/server', - }, - }, - { - name: 'foreceipt', - dirName: 'connector-foreceipt', - varName: 'connectorForeceipt', - imports: { - def: '@openint/connector-foreceipt/def', - server: '@openint/connector-foreceipt/server', - }, - }, { name: 'fs', dirName: 'connector-fs', @@ -259,15 +222,6 @@ module.exports = [ server: '@openint/connector-microsoft/server', }, }, - { - name: 'mongodb', - dirName: 'connector-mongodb', - varName: 'connectorMongodb', - imports: { - def: '@openint/connector-mongodb/def', - server: '@openint/connector-mongodb/server', - }, - }, { name: 'moota', dirName: 'connector-moota', diff --git a/apps/app-config/package.json b/apps/app-config/package.json index ae53dd56..faedf123 100644 --- a/apps/app-config/package.json +++ b/apps/app-config/package.json @@ -12,8 +12,6 @@ "dependencies": { "@clerk/nextjs": "5.6.0", "@openint/cdk": "workspace:*", - "@openint/connector-aircall": "workspace:*", - "@openint/connector-airtable": "workspace:*", "@openint/connector-alphavantage": "workspace:*", "@openint/connector-apollo": "workspace:*", "@openint/connector-beancount": "workspace:*", @@ -24,8 +22,6 @@ "@openint/connector-discord": "workspace:*", "@openint/connector-expensify": "workspace:*", "@openint/connector-finch": "workspace:*", - "@openint/connector-firebase": "workspace:*", - "@openint/connector-foreceipt": "workspace:*", "@openint/connector-fs": "workspace:*", "@openint/connector-github": "workspace:*", "@openint/connector-gong": "workspace:*", @@ -42,7 +38,6 @@ "@openint/connector-mercury": "workspace:*", "@openint/connector-merge": "workspace:*", "@openint/connector-microsoft": "workspace:*", - "@openint/connector-mongodb": "workspace:*", "@openint/connector-moota": "workspace:*", "@openint/connector-onebrick": "workspace:*", "@openint/connector-outreach": "workspace:*", diff --git a/apps/cli/package.json b/apps/cli/package.json index a94af981..d46ab1a1 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -9,7 +9,6 @@ "@openint/connector-beancount": "workspace:*", "@openint/connector-brex": "workspace:*", "@openint/connector-expensify": "workspace:*", - "@openint/connector-firebase": "workspace:*", "@openint/connector-fs": "workspace:*", "@openint/connector-heron": "workspace:*", "@openint/connector-lunchmoney": "workspace:*", @@ -33,7 +32,7 @@ "@openint/meta-service-airbyte": "workspace:*", "@openint/meta-service-postgres": "workspace:*", "@openint/util": "workspace:*", - "@trpc/server": "10.40.0", + "@trpc/server": "10.45.2", "cac": "6.7.12", "keytar": "7.9.0", "micro": "9.3.5-canary.3", diff --git a/apps/web/.env.example b/apps/web/.env.example index 88e26047..8048abc6 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -69,4 +69,10 @@ CLERK_SECRET_KEY="xxx" NEXT_PUBLIC_NANGO_PUBLIC_KEY="xxx" NANGO_SECRET_KEY="xxx" + +NEXT_PUBLIC_SERVER_URL="https://local.openint.dev" +NEXT_PUBLIC_API_URL="https://local.openint.dev" + +## Can also be "edge" for cloudflare workers +NEXT_PUBLIC_RUNTIME_ENV="standard" INTEGRATION_TEST_SECRET="xxx" diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 73cbd16c..29af548b 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -16,6 +16,8 @@ # next.js /.next/ /out/ +.open-next/ +.next-bak # production /build diff --git a/apps/web/__tests__/end-to-end.spec.ts b/apps/web/__tests__/end-to-end.spec.ts deleted file mode 100644 index 86e35ea9..00000000 --- a/apps/web/__tests__/end-to-end.spec.ts +++ /dev/null @@ -1,570 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import plaidSdkDef, {initPlaidSDK} from '@opensdks/sdk-plaid' -import {drizzle, eq, schema, sql} from '@openint/db' -import {createAppTrpcClient} from '@openint/engine-frontend/lib/trpcClient' -import {env, testEnv, testEnvRequired} from '@openint/env' -import {initOpenIntSDK} from '@openint/sdk' -import {setupTestOrg, tearDownTestOrg} from './test-utils' - -jest.setTimeout(30 * 1000) // long timeout because we have to wait for next.js to compile - -let fixture: Awaited> -let sdk: ReturnType - -let trpc: ReturnType - -const db = drizzle(env.DATABASE_URL, {logger: true, schema}) -async function setupTestDb(dbName: string) { - await db.execute(`DROP DATABASE IF EXISTS ${dbName}`) - await db.execute(`CREATE DATABASE ${dbName}`) - const url = new URL(env.DATABASE_URL) - url.pathname = `/${dbName}` - console.log('setupTestDb url:', url.toString()) - console.log(url.toString()) - return {url, db: drizzle(url.toString(), {logger: true})} -} - -beforeAll(async () => { - fixture = await setupTestOrg() - sdk = initOpenIntSDK({ - headers: {'x-apikey': fixture.apiKey}, - // TODO: also test with end user authentication token - baseUrl: 'http://localhost:4000/api/v0', - }) - trpc = createAppTrpcClient({ - apiUrl: 'http://localhost:4000/api/trpc', - // TODO: also test with end user authentication token - headers: {'x-apikey': fixture.apiKey}, - }) -}) - -afterAll(async () => { - if (!testEnv.DEBUG) { - await tearDownTestOrg(fixture) - await testDb.db.$client.end() - // Cannot drop because database connection is still kept open by connector-postgres/server - // await db.execute(`DROP DATABASE IF EXISTS test_${fixture.testId}`) - } -}) - -let testDb: Awaited> - -test('setup organization default destination', async () => { - testDb = await setupTestDb(`test_${fixture.testId}`) - // TODO: Add a separate test for syncing into public schema vs custom schema - testDb.url.searchParams.set('schema', 'openint') - - await sdk.PATCH('/viewer/organization', { - body: { - publicMetadata: { - database_url: testDb.url.toString(), - }, - }, - }) - - const org = await sdk.GET('/viewer/organization').then((r) => r.data) - expect(org.publicMetadata.database_url).toEqual(testDb.url.toString()) -}) - -test('create and sync plaid connection', async () => { - const connConfig = await sdk - .POST('/core/connector_config', { - body: { - orgId: fixture.org.id, - connectorName: 'plaid', - config: { - envName: 'sandbox', - products: ['transactions'], - countryCodes: ['US'], - language: 'en', - clientName: 'OpenInt Test', - credentials: null, // use platform credentials - }, - defaultPipeOut: { - streams: {account: true, transaction: true}, - links: ['unified_banking'], - }, - }, - }) - .then((r) => r.data) - - const plaid = initPlaidSDK({ - baseUrl: plaidSdkDef.oasMeta.servers[1].url, - headers: { - 'PLAID-CLIENT-ID': testEnvRequired.ccfg_plaid__CLIENT_ID, - 'PLAID-SECRET': testEnvRequired.ccfg_plaid__CLIENT_SECRET_SANDBOX, - }, - }) - const sandboxTokenRes = await plaid['/sandbox/public_token/create'].POST({ - body: {initial_products: ['transactions'], institution_id: 'ins_21'}, - }) - - const postConnectRes = await trpc.postConnect.mutate([ - { - publicToken: sandboxTokenRes.data.public_token, - meta: { - link_session_id: '327157e8-b91d-49da-aaf7-bd93b3adea31', - institution: { - name: 'Huntington Bank', - institution_id: 'ins_21', - }, - accounts: [], - }, - }, - connConfig.id, - {}, - ]) - - const connId = postConnectRes.connectionId! - - await sdk.POST('/core/connection/{id}/_sync', { - body: {async: false}, - params: {path: {id: connId}}, - }) - - const rows = await testDb.db.execute( - sql`SELECT * FROM openint.banking_transaction`, - ) - expect(rows[0]).toMatchObject({ - source_id: connId, - id: expect.any(String), - customer_id: null, - created_at: expect.any(String), - updated_at: expect.any(String), - connector_name: 'plaid', - unified: expect.any(Object), - raw: expect.any(Object), - }) - - if (!testEnv.DEBUG) { - await sdk.DELETE('/core/connection/{id}', {params: {path: {id: connId}}}) - } -}) - -test('create and sync greenhouse connection', async () => { - const connConfig = await sdk - .POST('/core/connector_config', { - body: { - orgId: fixture.org.id, - connectorName: 'greenhouse', - defaultPipeOut: { - streams: {job: true, candidate: true}, - // TODO: Get sync with no unification to also work - // May need to work around drizzle-kit issues though - links: ['unified_ats'], - }, - }, - }) - .then((r) => r.data) - - const body = { - connectorConfigId: connConfig.id, - settings: {apiKey: testEnv.conn_greenhouse_API_KEY}, - displayName: 'displayName', - disabled: false, - metadata: { - key: 'value', - }, - customerId: 'fooCustomerId', - } - const {data: connId} = await sdk.POST('/core/connection', { - body, - }) - console.log('[endToEnd] connId', connId) - - const syncRes = await sdk.POST('/core/connection/{id}/_sync', { - body: {async: false}, - params: {path: {id: connId}}, - }) - - const rows = await testDb.db.execute(sql`SELECT * FROM openint.job`) - expect(rows[0]).toMatchObject({ - source_id: connId, - id: expect.any(String), - customer_id: null, - created_at: expect.any(String), - updated_at: expect.any(String), - connector_name: 'greenhouse', - unified: expect.any(Object), - raw: expect.any(Object), - }) - - const syncCompletedEvent = await db.query.event.findFirst({ - where: eq( - schema.event.id, - syncRes.data.pipeline_syncs?.[0]?.sync_completed_event_id ?? '', - ), - }) - expect(syncCompletedEvent).toMatchObject({ - name: 'sync.completed', - data: {source_id: connId}, - }) -}) - -test('list connections', async () => { - const res = await sdk.GET('/core/connection') - - expect(res.data).toHaveLength(2) - // Check we have the default postgres connector - expect( - res.data.find((c) => c.connectorConfigId.includes('ccfg_postgres_default')), - ).toBeTruthy() - // Check we have the greenhouse connector - expect( - res.data.find((c) => c.connectorConfigId.includes('ccfg_greenhouse')), - ).toBeTruthy() - - // Check connection object structure - expect(res.data[0]).toHaveProperty('connectorName') - expect(res.data[0]).toHaveProperty('displayName') - expect(res.data[0]).toHaveProperty('connectorConfigId') - expect(res.data[0]).toHaveProperty('integrationId') - expect(res.data[0]).toHaveProperty('settings') - expect(res.data[0]).toHaveProperty('metadata') -}) - -test('list connector metas', async () => { - const res = await sdk.GET('/connector') - const connectorData = res.data as Record - const connector = connectorData['greenhouse'] - - expect(Object.keys(connectorData).length).toBeGreaterThan(40) - // Check basic properties - expect(connector).toHaveProperty('__typename', 'connector') - expect(connector).toHaveProperty('name') - expect(connector).toHaveProperty('displayName') - expect(connector).toHaveProperty('logoUrl') - expect(connector).toHaveProperty('stage') - expect(connector).toHaveProperty('verticals') - expect(connector).toHaveProperty('supportedModes') - expect(connector).toHaveProperty('schemas') -}) - -test('list connector config info', async () => { - const res = await sdk.GET('/core/connector_config_info') - - expect(res.data).toHaveLength(2) - // Check we have the default postgres connector - expect(res.data.find((c) => c.connectorName.includes('plaid'))).toBeTruthy() - // Check we have the greenhouse connector - expect( - res.data.find((c) => c.connectorName.includes('greenhouse')), - ).toBeTruthy() - - // Check basic properties - expect(res.data[0]).toHaveProperty('envName') - expect(res.data[0]).toHaveProperty('displayName') - expect(res.data[0]).toHaveProperty('connectorName') - expect(res.data[0]).toHaveProperty('isSource') - expect(res.data[0]).toHaveProperty('isDestination') - expect(res.data[0]).toHaveProperty('verticals') - expect(res.data[0]).toHaveProperty('integrations') -}) - -test('list configured integrations', async () => { - const res = await sdk.GET('/configured_integrations') - - expect(res.data.items).toHaveLength(1) - expect(res.data.items[0]?.connector_name).toEqual('plaid') - - // Check basic properties - expect(res.data.items[0]).toHaveProperty('connector_config_id') - expect(res.data.items[0]).toHaveProperty('connector_name') - expect(res.data.items[0]).toHaveProperty('logo_url') - expect(res.data.items[0]).toHaveProperty('name') -}) - -test('create token', async () => { - const res = await sdk.POST('/connect/token', { - body: { - customerId: fixture.org.id, - validityInSeconds: 600, - }, - }) - expect(res.data).toHaveProperty('token') -}) - -test('create connector config with bad parameters fail', async () => { - await expect( - sdk.POST('/core/connector_config', { - body: { - orgId: fixture.org.id, - connectorName: 'foo', - defaultPipeOut: { - streams: {job: true, candidate: true}, - }, - }, - }), - ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.POST('/core/connector_config', { - // body: { - // orgId: fixture.org.id, - // connectorName: 'greenhouse', - // defaultPipeOut: { - // streams: {michael: true}, - // }, - // }, - // }), - // ).rejects.toThrow() - - await expect( - sdk.POST('/core/connector_config', { - body: { - orgId: 'micasa', - connectorName: 'greenhouse', - defaultPipeOut: { - streams: {job: true, candidate: true}, - }, - }, - }), - ).rejects.toThrow() -}) - -test('Edit organization with bad parameters fails', async () => { - // TODO ? this should fail ? - // const org1 = await sdk.PATCH('/viewer/organization', { - // body: { - // publicMetadata: { - // database_url: testDb.url.toString(), - // // @ts-expect-error - // foo: 'value', - // }, - // }, - // }) - - // expect(org1.error).toBeTruthy() - - await expect( - sdk.PATCH('/viewer/organization', { - body: { - publicMetadata: { - // @ts-expect-error - database_url: {}, - }, - }, - }), - ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.PATCH('/viewer/organization', { - // body: { - // publicMetadata: { - // // @ts-expect-error - // migrate_tables: {}, - // }, - // }, - // }), - // ).rejects.toThrow() - - await expect( - sdk.PATCH('/viewer/organization', { - // @ts-expect-error - body: {}, - }), - ).rejects.toThrow() -}) - -test('create connections with bad parameters', async () => { - // const randomString = (n: number) => { - // let str = '' - // for (let i = 0; i < n; i++) { - // str += 'a' - // } - // return str - // } - const connConfig = await sdk - .POST('/core/connector_config', { - body: { - orgId: fixture.org.id, - connectorName: 'greenhouse', - }, - }) - .then((r) => r.data) - - // TODO: validate API keys for greenhouse when adding a connection with it - // await expect( - // sdk.POST('/core/connection', { - // body: { - // connectorConfigId: connConfig.id, - // settings: {apiKey: 'fooApiKey'}, - // }, - // }), - // ).rejects.toThrow() - - await expect( - sdk.POST('/core/connection', { - // @ts-expect-error - body: {}, - }), - ).rejects.toThrow() - - const body = { - connectorConfigId: connConfig.id, - settings: {apiKey: testEnv.conn_greenhouse_API_KEY}, - displayName: 'displayName', - disabled: false, - metadata: { - key: 'value', - }, - } - - await expect( - sdk.POST('/core/connection', { - body: { - ...body, - // @ts-expect-error - settings: 'foo', - }, - }), - ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.POST('/core/connection', { - // body: { - // ...body, - // displayName: randomString(9999), - // }, - // }), - // ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.POST('/core/connection', { - // body: { - // ...body, - // // @ts-expect-error - // disabled: {}, - // }, - // }), - // ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.POST('/core/connection', { - // body: { - // ...body, - // metadata: 'foo', - // }, - // }), - // ).rejects.toThrow() - - // TODO: this should fail - // await expect( - // sdk.POST('/core/connection', { - // body: { - // ...body, - // customerId: randomString(999), - // }, - // }), - // ).rejects.toThrow() - - await expect( - sdk.POST('/core/connection', { - body: { - ...body, - integrationId: 'foo', - }, - }), - ).rejects.toThrow() -}) - -// Group: /health -describe('/health', () => { - test('GET /health - Health check', async () => { - const res = await sdk.GET('/health') - expect(res.response.status).toEqual(200) - expect(res.data).toMatchObject({healthy: true}) - }) - - // TODO: fix this, failing with - // "message": "unrecognized configuration parameter \"schema\"", - // test('GET /health?exp=true - Health check with query params', async () => { - // const res = await sdk.GET('/health', { - // params: { - // query: {exp: true}, - // }, - // }) - // expect(res.response.status).toEqual(200) - // expect(res.data.healthy).toBe(true) - // }) -}) - -// Group: /clerk-testing-token -describe('/clerk-testing-token', () => { - test('GET /clerk-testing-token - Returns a testing token', async () => { - const res = await sdk.GET('/clerk-testing-token', { - params: { - query: {secret: testEnv.INTEGRATION_TEST_SECRET + ''}, - }, - }) - expect(res.response.status).toEqual(200) - expect(res.data).toHaveProperty('testing_token') - }) -}) - -// Group: /connect -describe('/connect', () => { - test('POST /connect/token - Creates a connect token', async () => { - const res = await sdk.POST('/connect/token', { - body: { - customerId: 'testCustomer', - validityInSeconds: 600, - }, - }) - expect(res.response.status).toEqual(200) - expect(res.data).toHaveProperty('token') - }) - - test('POST /connect/magic-link - Creates a magic link', async () => { - const res = await sdk.POST('/connect/magic-link', { - body: { - customerId: 'testCustomer', - displayName: 'Test', - redirectUrl: 'https://example.com', - }, - }) - expect(res.response.status).toEqual(200) - expect(res.data).toHaveProperty('url') - }) -}) - -// Group: /passthrough -describe('/passthrough', () => { - // TODO: add passthrough positive case - - test('POST /passthrough - Throws error for greenhouse passthrough request', async () => { - const connConfig = await sdk - .POST('/core/connector_config', { - body: { - orgId: fixture.org.id, - connectorName: 'greenhouse', - }, - }) - .then((r) => r.data) - - // TODO: validate API keys for greenhouse when adding a connection with it - const {data: connId} = await sdk.POST('/core/connection', { - body: { - connectorConfigId: connConfig.id, - settings: {apiKey: 'fooApiKey'}, - }, - }) - - await expect(async () => { - await sdk.POST('/passthrough', { - body: { - method: 'GET', - path: 'https://jsonplaceholder.typicode.com/todos/1', - }, - headers: { - 'x-connection-id': connId, - }, - }) - }).rejects.toThrow() - }) -}) diff --git a/apps/web/app/api/connections/[connectionId]/sql/route.ts b/apps/web/app/api/connections/[connectionId]/sql/route.ts deleted file mode 100644 index c532a1b9..00000000 --- a/apps/web/app/api/connections/[connectionId]/sql/route.ts +++ /dev/null @@ -1,97 +0,0 @@ -import {TRPCError} from '@trpc/server' -import type {NextRequest} from 'next/server' -import {NextResponse} from 'next/server' -import {contextFactory, Papa} from '@openint/app-config/backendConfig' -import {__DEBUG__, kAcceptUrlParam} from '@openint/app-config/constants' -import type {Id} from '@openint/cdk' -import {hasRole} from '@openint/cdk' -import {zPgConfig} from '@openint/connector-postgres' -import {drizzle} from '@openint/db' -import {R, z} from '@openint/util' -import {serverComponentGetViewer} from '@/lib-server/server-component-helpers' -import {trpcErrorResponse} from '@/lib-server/server-helpers' - -// TODO: Document searchParams and share it with client (as least the typing if nothing else) - -export async function GET( - request: NextRequest, - {params: {connectionId}}: {params: {connectionId: string}}, -) { - try { - const { - q: query, - [kAcceptUrlParam]: acceptedFormat, - dl: download, - } = Object.fromEntries(request.nextUrl.searchParams.entries()) - - if (!query) { - return new NextResponse('sql query param q is required', {status: 400}) - } - const format = z.enum(['csv', 'json']).default('json').parse(acceptedFormat) - - const viewer = await serverComponentGetViewer(request.nextUrl) - if (!hasRole(viewer, ['user', 'org', 'system'])) { - throw new TRPCError({ - code: viewer.role === 'anon' ? 'UNAUTHORIZED' : 'FORBIDDEN', - }) - } - - const {services} = contextFactory.fromViewer(viewer) - const conn = await services.getConnectionOrFail(connectionId as Id['conn']) - - if (conn.connectorName !== 'postgres') { - throw new TRPCError({ - code: 'PRECONDITION_FAILED', - message: 'Only postgres connections are supported', - }) - } - const pgConfig = zPgConfig.parse(conn.settings) - - const db = drizzle(pgConfig.databaseUrl, {logger: __DEBUG__}) - - console.log('[sql] Will run query for user', {query, viewer}) - - // TODO: Should we limit admin user to RLS also? Otherwise we might as well - // proxy the pgMeta endpoint just like we proxy rest / graphql - - const rows = await db.execute(query) - const res = - format === 'csv' - ? new NextResponse( - Papa.unparse([ - ...rows.map((r) => - R.mapValues(r, (v) => - v instanceof Date - ? v.toISOString() - : typeof v === 'object' - ? JSON.stringify(v) - : v, - ), - ), - ]), - { - // headers: {'Content-Type': 'text/csv'}, - }, - ) - : NextResponse.json(rows) - - if (download) { - // TODO: Better filename would be nice. - res.headers.set( - 'Content-Disposition', - `attachment; filename="openint-${Date.now()}.${format}"`, - ) - } - return res - } catch (err) { - // TODO: Fix me to use postgres.js error instead. - // DatabaseError is most likely a result of invalid sql syntax - // if (err instanceof DatabaseError) { - // return new NextResponse(err.message, {status: 400}) - // } - if (err instanceof TRPCError) { - return trpcErrorResponse(err) - } - throw err - } -} diff --git a/apps/web/app/api/v0-edge/[...trpc]/route.ts b/apps/web/app/api/v0-edge/[...trpc]/route.ts deleted file mode 100644 index 1f16df83..00000000 --- a/apps/web/app/api/v0-edge/[...trpc]/route.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {modifyRequest, modifyResponse} from '@opensdks/fetch-links' - -// edge runtime does not have a max duration but must return response within 25 seconds then must begin streaming -// This means we should explore running more things (like sync) on top fo the edge runtime and stream messages back -export const runtime = 'edge' - -// Used to workaround issues where next.js node handler does not support incoming request with -// transfer-encoding: chunked -const handler = async (req: Request) => { - const body = ['POST', 'PUT', 'PATCH'].includes(req.method) - ? await req.blob() - : null - console.log('[v0-edge] Request', {url: req.url, method: req.method}) - - const modifiedReq = modifyRequest(req, { - url: req.url.replace('/api/v0-edge', '/api/v0'), - headers: (h) => { - if (body) { - h.set('content-length', body.size.toString()) - } - h.delete('transfer-encoding') - }, - body, - }) - const res = await fetch(modifiedReq) - - console.log('[v0-edge] response', res.status, res.headers) - - // otherwise crash if header exists... - return modifyResponse(res, {headers: (h) => h.delete('x-middleware-rewrite')}) -} - -export { - handler as GET, - handler as PUT, - handler as POST, - handler as DELETE, - handler as OPTIONS, - handler as HEAD, - handler as PATCH, - handler as TRACE, -} diff --git a/apps/web/app/dashboard/(authenticated)/Sidebar.tsx b/apps/web/app/dashboard/(authenticated)/Sidebar.tsx index 5c583ab4..10594d9a 100644 --- a/apps/web/app/dashboard/(authenticated)/Sidebar.tsx +++ b/apps/web/app/dashboard/(authenticated)/Sidebar.tsx @@ -63,11 +63,14 @@ interface SidebarProps extends React.HTMLAttributes { hasPgConnection: boolean } +const currentUrl = typeof window !== 'undefined' ? window.location.href : '' + export function Sidebar({className, hasPgConnection}: SidebarProps) { const pathname = usePathname() - const links = hasPgConnection - ? sectionedLinks - : sectionedLinks.filter((s) => s.title !== 'Console') + const links = + hasPgConnection && currentUrl.includes('openint.dev') + ? sectionedLinks + : sectionedLinks.filter((s) => s.title !== 'Console') return (