diff --git a/apps/web/app/(admin)/(authenticated)/resources/ResourcesPage.tsx b/apps/web/app/(admin)/(authenticated)/resources/ResourcesPage.tsx index 35ab0589..65c8ee86 100644 --- a/apps/web/app/(admin)/(authenticated)/resources/ResourcesPage.tsx +++ b/apps/web/app/(admin)/(authenticated)/resources/ResourcesPage.tsx @@ -5,7 +5,9 @@ import Image from 'next/image' import Link from 'next/link' import React from 'react' +import {env} from '@usevenice/app-config/env' import {clientIntegrations} from '@usevenice/app-config/integrations/integrations.client' +import {defIntegrations} from '@usevenice/app-config/integrations/integrations.def' import {extractProviderName, zRaw} from '@usevenice/cdk-core' import type {RouterOutput} from '@usevenice/engine-backend' import {_trpcReact, VeniceConnectButton} from '@usevenice/engine-frontend' @@ -54,7 +56,11 @@ export default function ResourcesPage() {

Resources

- +

Resources are created based on integration configurations

+ return ( + + ) } diff --git a/packages/cdk-core/integration.types.ts b/packages/cdk-core/integration.types.ts index 3aa2b461..8f46148f 100644 --- a/packages/cdk-core/integration.types.ts +++ b/packages/cdk-core/integration.types.ts @@ -1,7 +1,7 @@ import type {MaybePromise, z} from '@usevenice/util' import {R} from '@usevenice/util' -import type {EndUserId} from './id.types' +import type {EndUserId, Id} from './id.types' import {makeId} from './id.types' import type {ZStandard} from './meta.types' import type { @@ -100,7 +100,10 @@ export interface IntegrationClient< openDialog: OpenDialogFn }) => ( connectInput: T['_types']['connectInput'], - context: ConnectOptions, + context: ConnectOptions & { + // TODO: Does this belong here? + integrationId: Id['int'] + }, ) => Promise } diff --git a/packages/cdk-core/nango.ts b/packages/cdk-core/nango.ts index dd198975..74454f56 100644 --- a/packages/cdk-core/nango.ts +++ b/packages/cdk-core/nango.ts @@ -154,7 +154,9 @@ export const zIntegration = zIntegrationShort.extend({ client_secret: z.string(), scopes: z.string(), app_link: z.string().nullish(), - auth_mode: z.enum(['OAUTH2', 'OAUTH1', 'BASIC']), + // In practice we only use nango for oauth integrations + // but in theory we could use it for a generic secret store as well + auth_mode: z.enum(['OAUTH2', 'OAUTH1', 'BASIC', 'API_KEY']), }) export const zUpsertIntegration = zIntegration diff --git a/packages/cdk-core/providers.types.ts b/packages/cdk-core/providers.types.ts index 350afe17..d768d55c 100644 --- a/packages/cdk-core/providers.types.ts +++ b/packages/cdk-core/providers.types.ts @@ -7,7 +7,7 @@ import { zodToJsonSchema, } from '@usevenice/util' -import type {EndUserId, ExtEndUserId, ExternalId} from './id.types' +import type {EndUserId, ExtEndUserId, ExternalId, Id} from './id.types' import {zExternalId} from './id.types' import type { AnyIntegrationImpl, @@ -102,7 +102,10 @@ export type UseConnectHook = (scope: { openDialog: OpenDialogFn }) => ( connectInput: T['_types']['connectInput'], - context: ConnectOptions, + context: ConnectOptions & { + // TODO: Does this belong here? + integrationId: Id['int'] + }, ) => Promise // MARK: - Server side connect types diff --git a/packages/engine-frontend/VeniceConnect.tsx b/packages/engine-frontend/VeniceConnect.tsx index a80e9ebb..48b5042b 100644 --- a/packages/engine-frontend/VeniceConnect.tsx +++ b/packages/engine-frontend/VeniceConnect.tsx @@ -1,5 +1,6 @@ 'use client' +import Nango from '@nangohq/frontend' import {useMutation} from '@tanstack/react-query' import {Link2, Loader2, RefreshCw, Trash2} from 'lucide-react' import React from 'react' @@ -7,6 +8,7 @@ import React from 'react' import type { Id, IntegrationClient, + IntegrationDef, OpenDialogFn, UseConnectHook, } from '@usevenice/cdk-core' @@ -47,9 +49,12 @@ import {_trpcReact} from './TRPCProvider' type ConnectEventType = 'open' | 'close' | 'error' export interface VeniceConnectProps extends UIPropsNoChildren { + /** Does this belong here? */ + nangoPublicKey: string /** Whether to display the existing connections */ showExisting?: boolean clientIntegrations: Record + defIntegrations: Record onEvent?: (event: {type: ConnectEventType; intId: Id['int']}) => void } @@ -132,15 +137,22 @@ type ProviderMeta = Catalog[string] export function _VeniceConnect({ catalog, clientIntegrations, + defIntegrations, onEvent, showExisting, className, integrationIds, + nangoPublicKey, ...uiProps }: VeniceConnectProps & { integrationIds: Array catalog: Catalog }) { + const nango = React.useMemo( + () => new Nango({publicKey: nangoPublicKey}), + [nangoPublicKey], + ) + // VeniceConnect should be fetching its own integrationIds as well as resources // this way it can esure those are refreshed as operations take place // This is esp true when we are operating in client envs (js embed) @@ -194,10 +206,21 @@ export function _VeniceConnect({ integrationIds, R.map(extractProviderName), R.uniq, - R.mapToObj((name: string) => [ - name, - clientIntegrations[name]?.useConnectHook?.({openDialog}), - ]), + R.mapToObj((name: string) => { + let fn = clientIntegrations[name]?.useConnectHook?.({openDialog}) + const nangoProvider = defIntegrations[name]?.metadata?.nangoProvider + if (!fn && nangoProvider) { + console.log('adding nnango provider for', nangoProvider) + + fn = async (_, {integrationId}) => { + console.log('inputs', integrationId) + await nango.auth(integrationId, 'conn_test').then((r) => { + console.log('auth', r) + }) + } + } + return [name, fn] + }), ) const categories = zIntegrationCategory.options @@ -359,7 +382,7 @@ export const WithProviderConnect = ({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const connOutput = connectFn - ? await connectFn?.(connInput, {}) + ? await connectFn?.(connInput, {integrationId: int.id}) : connInput console.log(`[VeniceConnect] ${int.id} connOutput`, connOutput) diff --git a/packages/engine-frontend/package.json b/packages/engine-frontend/package.json index 6b5f84a0..2e522986 100644 --- a/packages/engine-frontend/package.json +++ b/packages/engine-frontend/package.json @@ -5,6 +5,7 @@ "sideEffects": false, "module": "./index.ts", "dependencies": { + "@nangohq/frontend": "0.33.8", "@tanstack/react-query": "*", "@trpc/client": "10.21.1", "@trpc/react-query": "10.21.1", diff --git a/packages/util/zod-jsonschema-utils.ts b/packages/util/zod-jsonschema-utils.ts index ac2b7afd..6d451502 100644 --- a/packages/util/zod-jsonschema-utils.ts +++ b/packages/util/zod-jsonschema-utils.ts @@ -15,7 +15,7 @@ export function defaultTitleAsJsonPath(jsonSchema: T) { .filter((n) => !!n) // Filter out nesting from things like anyOf .join('.') - if (node.title) { + if (node.title && jsonPath) { // @see https://share.cleanshot.com/16sDgL6D node.title = `${jsonPath}: ${node.title}` } else if (jsonPath && !jsonPath.endsWith('.')) { diff --git a/packages/util/zod-jsonschema.spec.ts b/packages/util/zod-jsonschema.spec.ts index 019b6acd..2c2c0d81 100644 --- a/packages/util/zod-jsonschema.spec.ts +++ b/packages/util/zod-jsonschema.spec.ts @@ -140,11 +140,11 @@ test('enum description to title 2', () => { "clientId", "clientSecret", ], - "title": "Enable", + "title": "oauth: Enable", "type": "object", }, { - "title": "Disable", + "title": "oauth: Disable", "type": "null", }, ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae454471..fdeb4230 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1316,6 +1316,9 @@ importers: packages/engine-frontend: dependencies: + '@nangohq/frontend': + specifier: 0.33.8 + version: 0.33.8 '@tanstack/react-query': specifier: 4.28.0 version: 4.28.0(react-dom@18.2.0)(react@18.2.0) @@ -3880,6 +3883,10 @@ packages: engines: {node: '>=12'} dev: false + /@nangohq/frontend@0.33.8: + resolution: {integrity: sha512-iKTo4BDEXaKG6FbZzT9iACr07lGDBhmaS90+tsd92XrFpO8Gk7JGMp13lUS66mfwTcCXqPBXFVSkBvE0QyJi8Q==} + dev: false + /@next/env@13.4.3: resolution: {integrity: sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ==} dev: false