Skip to content

Commit

Permalink
[WIP] Got popup open for QBO authentication via nango
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyxiao committed Oct 12, 2023
1 parent 10d885c commit 1b38b0f
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -54,7 +56,11 @@ export default function ResourcesPage() {
<h2 className="mb-4 mr-auto text-2xl font-semibold tracking-tight">
Resources
</h2>
<VeniceConnectButton clientIntegrations={clientIntegrations} />
<VeniceConnectButton
defIntegrations={defIntegrations}
clientIntegrations={clientIntegrations}
nangoPublicKey={env.NEXT_PUBLIC_NANGO_PUBLIC_KEY}
/>
</header>
<p>Resources are created based on integration configurations</p>
<DataTable
Expand Down
11 changes: 10 additions & 1 deletion apps/web/app/connect/ConnectPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
'use client'

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 {VeniceConnect} from '@usevenice/engine-frontend'

/**
* Only reason this file exists is because we cannot pass clientIntegrations directly
* from a server component because it contains function references (i.e. useConnectHook)
*/
export default function ConnectPage() {
return <VeniceConnect clientIntegrations={clientIntegrations} showExisting />
return (
<VeniceConnect
clientIntegrations={clientIntegrations}
defIntegrations={defIntegrations}
showExisting
nangoPublicKey={env.NEXT_PUBLIC_NANGO_PUBLIC_KEY}
/>
)
}
7 changes: 5 additions & 2 deletions packages/cdk-core/integration.types.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<T['_types']['connectOutput']>
}

Expand Down
4 changes: 3 additions & 1 deletion packages/cdk-core/nango.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions packages/cdk-core/providers.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -102,7 +102,10 @@ export type UseConnectHook<T extends IntHelpers = IntHelpers> = (scope: {
openDialog: OpenDialogFn
}) => (
connectInput: T['_types']['connectInput'],
context: ConnectOptions,
context: ConnectOptions & {
// TODO: Does this belong here?
integrationId: Id['int']
},
) => Promise<T['_types']['connectOutput']>

// MARK: - Server side connect types
Expand Down
33 changes: 28 additions & 5 deletions packages/engine-frontend/VeniceConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'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'

import type {
Id,
IntegrationClient,
IntegrationDef,
OpenDialogFn,
UseConnectHook,
} from '@usevenice/cdk-core'
Expand Down Expand Up @@ -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<string, IntegrationClient>
defIntegrations: Record<string, IntegrationDef>
onEvent?: (event: {type: ConnectEventType; intId: Id['int']}) => void
}

Expand Down Expand Up @@ -132,15 +137,22 @@ type ProviderMeta = Catalog[string]
export function _VeniceConnect({
catalog,
clientIntegrations,
defIntegrations,
onEvent,
showExisting,
className,
integrationIds,
nangoPublicKey,
...uiProps
}: VeniceConnectProps & {
integrationIds: Array<Id['int']>
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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions packages/engine-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1b38b0f

Please sign in to comment.