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 9f65c73
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 15 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
2 changes: 1 addition & 1 deletion packages/util/zod-jsonschema-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function defaultTitleAsJsonPath<T = unknown>(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('.')) {
Expand Down
4 changes: 2 additions & 2 deletions packages/util/zod-jsonschema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
],
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 9f65c73

Please sign in to comment.