Skip to content

Commit 3ca38d1

Browse files
authored
feat(v1): API remaining methods and docs improvements (#292)
* adding connect router * adding openapi descriptions * fixing token generation * reenabling health and viewer endpoitns * improving input types
1 parent dd4f981 commit 3ca38d1

File tree

8 files changed

+4212
-364
lines changed

8 files changed

+4212
-364
lines changed

packages-v1/api-v1/__generated__/openapi.json

+3,334-344
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages-v1/api-v1/__generated__/openapi.types.ts

+689-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import {z} from 'zod'
2+
import {makeJwtClient, zCustomerId, zId} from '@openint/cdk'
3+
import {publicProcedure, router} from '../_base'
4+
import {getServerUrl} from '../../../../apps/app-config/constants'
5+
import {asCustomer} from '../../../../packages/engine-backend/router/customerRouter'
6+
import {zConnectorName} from './connection'
7+
8+
export const connectRouter = router({
9+
createMagicLink: publicProcedure
10+
.meta({
11+
openapi: {
12+
method: 'POST',
13+
path: '/connect/magic-link',
14+
description: 'Create a magic link for connecting integrations',
15+
},
16+
})
17+
.input(
18+
z.object({
19+
email: z.string().email().describe('The email address of the customer'),
20+
customer_id: (zCustomerId as any).describe(
21+
'Anything that uniquely identifies the customer that you will be sending the magic link to',
22+
),
23+
validity_in_seconds: z
24+
.number()
25+
.optional()
26+
.default(2592000)
27+
.describe(
28+
'How long the magic link will be valid for (in seconds) before it expires',
29+
),
30+
redirect_url: z
31+
.string()
32+
.nullable()
33+
.optional()
34+
.describe(
35+
'Where to send user to after connect / if they press back button',
36+
),
37+
connector_names: zConnectorName
38+
.nullable()
39+
.optional()
40+
.describe('Filter integrations by comma separated connector names'),
41+
connection_id: (zId('conn') as any)
42+
.nullable()
43+
.optional()
44+
.describe('Filter managed connections by connection id'),
45+
theme: z
46+
.enum(['light', 'dark'])
47+
.nullable()
48+
.optional()
49+
.describe('Magic Link display theme'),
50+
view: z
51+
.enum(['manage', 'manage-deeplink', 'add', 'add-deeplink'])
52+
.nullable()
53+
.optional()
54+
.describe('Magic Link tab view'),
55+
}),
56+
)
57+
.output(z.object({url: z.string()}))
58+
.mutation(async ({ctx, input}) => {
59+
// TODO: replace with new signing and persisting mechanism
60+
const jwt = makeJwtClient({
61+
secretOrPublicKey: process.env['JWT_SECRET']!,
62+
})
63+
const token = jwt.signViewer(
64+
asCustomer(ctx.viewer, {customerId: input.customer_id as any}),
65+
{
66+
validityInSeconds: input.validity_in_seconds,
67+
},
68+
)
69+
70+
const url = new URL('/connect/portal', getServerUrl(null))
71+
url.searchParams.set('token', token)
72+
73+
if (input.redirect_url) {
74+
url.searchParams.set('redirectUrl', input.redirect_url)
75+
}
76+
77+
if (input.connector_names) {
78+
const connectorNames = input.connector_names
79+
.split(',')
80+
.map((name) => name.trim())
81+
url.searchParams.set('connectorNames', connectorNames.join(','))
82+
}
83+
84+
if (input.connection_id) {
85+
url.searchParams.set('connectionId', input.connection_id)
86+
}
87+
88+
if (input.theme) {
89+
url.searchParams.set('theme', input.theme)
90+
}
91+
92+
if (input.view) {
93+
url.searchParams.set('view', input.view)
94+
}
95+
96+
return {
97+
url: url.toString(),
98+
}
99+
}),
100+
createToken: publicProcedure
101+
.meta({
102+
openapi: {
103+
method: 'POST',
104+
path: '/connect/token',
105+
description: 'Create an authentication token for a customer',
106+
},
107+
})
108+
.input(
109+
z.object({
110+
customer_id: z
111+
.string()
112+
.min(1)
113+
.describe(
114+
'Anything that uniquely identifies the customer that you will be sending the token to',
115+
),
116+
validity_in_seconds: z
117+
.number()
118+
.positive()
119+
.optional()
120+
.default(2592000)
121+
.describe(
122+
'How long the token will be valid for (in seconds) before it expires',
123+
),
124+
}),
125+
)
126+
.output(z.object({token: z.string()}))
127+
.mutation(async ({ctx, input}) => {
128+
const jwt = makeJwtClient({
129+
secretOrPublicKey: process.env['JWT_SECRET']!,
130+
})
131+
132+
const token = jwt.signViewer(
133+
asCustomer(ctx.viewer, {customerId: input.customer_id as any}),
134+
{
135+
validityInSeconds: input.validity_in_seconds,
136+
},
137+
)
138+
139+
return {
140+
token: token,
141+
}
142+
}),
143+
})

packages-v1/api-v1/trpc/routers/connection.ts

+30-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {TRPCError} from '@trpc/server'
22
import {z} from 'zod'
33
import {defConnectors} from '@openint/all-connectors/connectors.def'
44
import {serverConnectors} from '@openint/all-connectors/connectors.server'
5+
import {zCustomerId, zId} from '@openint/cdk'
56
import {and, count, eq, schema} from '@openint/db'
67
import {publicProcedure, router, RouterContext} from '../_base'
78
import {core} from '../../models'
@@ -38,6 +39,10 @@ const zExpandOptions = z
3839
.enum(['connector'])
3940
.describe('Fields to expand: connector (includes connector details)')
4041

42+
export const zConnectorName = z
43+
.enum(Object.keys(serverConnectors) as [string, ...string[]])
44+
.describe('The name of the connector')
45+
4146
async function formatConnection(
4247
ctx: RouterContext,
4348
connection: z.infer<typeof core.connection>,
@@ -95,12 +100,16 @@ async function formatConnection(
95100
export const connectionRouter = router({
96101
getConnection: publicProcedure
97102
.meta({
98-
openapi: {method: 'GET', path: '/connection/{id}'},
103+
openapi: {
104+
method: 'GET',
105+
path: '/connection/{id}',
106+
description: 'Get details of a specific connection',
107+
},
99108
})
100109
// TODO: make zId('conn')
101110
.input(
102111
z.object({
103-
id: z.string(),
112+
id: zId('conn') as any,
104113
include_secrets: zIncludeSecrets.optional().default('none'),
105114
refresh_policy: zRefreshPolicy.optional().default('auto'),
106115
expand: z.array(zExpandOptions).optional().default([]),
@@ -143,15 +152,19 @@ export const connectionRouter = router({
143152
}),
144153
listConnections: publicProcedure
145154
.meta({
146-
openapi: {method: 'GET', path: '/connection'},
155+
openapi: {
156+
method: 'GET',
157+
path: '/connection',
158+
description: 'List all connections with optional filtering',
159+
},
147160
})
148161
.input(
149162
zListParams
150163
.extend({
151-
connector_name: z.string().optional(),
152-
customer_id: z.string().optional(),
164+
connector_name: zConnectorName.optional(),
165+
customer_id: zCustomerId.optional() as any,
153166
// TODO: make zId('ccfg').optional()
154-
connector_config_id: z.string().optional(),
167+
connector_config_id: zId('ccfg').optional() as any,
155168
include_secrets: zIncludeSecrets.optional().default('none'),
156169
expand: z.array(zExpandOptions).optional().default([]),
157170
})
@@ -174,11 +187,11 @@ export const connectionRouter = router({
174187
input.connector_config_id,
175188
)
176189
: undefined,
177-
input?.customer_id
178-
? eq(schema.connection.customer_id, input.customer_id)
190+
input?.['customer_id']
191+
? eq(schema.connection.customer_id, input['customer_id'])
179192
: undefined,
180-
input?.connector_name
181-
? eq(schema.connection.connector_name, input.connector_name)
193+
input?.['connector_name']
194+
? eq(schema.connection.connector_name, input['connector_name'])
182195
: undefined,
183196
),
184197
),
@@ -207,17 +220,21 @@ export const connectionRouter = router({
207220
}),
208221
checkConnection: publicProcedure
209222
.meta({
210-
openapi: {method: 'POST', path: '/connection/{id}/check'},
223+
openapi: {
224+
method: 'POST',
225+
path: '/connection/{id}/check',
226+
description: 'Verify that a connection is healthy',
227+
},
211228
})
212229
.input(
213230
z.object({
214231
// TODO: make zId('conn')
215-
id: z.string(),
232+
id: zId('conn') as any,
216233
}),
217234
)
218235
.output(
219236
z.object({
220-
id: z.string(),
237+
id: zId('conn') as any,
221238
status: zConnectionStatus,
222239
error: zConnectionError.optional(),
223240
errorMessage: z.string().optional(),

packages-v1/api-v1/trpc/routers/connectorConfig.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@ import {
99
zListParams,
1010
zListResponse,
1111
} from '../utils/pagination'
12+
import {zConnectorName} from './connection'
1213

1314
export const connectorConfigRouter = router({
1415
listConnectorConfigs: authenticatedProcedure
1516
.meta({
16-
openapi: {method: 'GET', path: '/connector-config'},
17+
openapi: {
18+
method: 'GET',
19+
path: '/connector-config',
20+
description:
21+
'List all connector configurations with optional filtering',
22+
},
1723
})
1824
.input(
1925
zListParams
2026
.extend({
2127
expand: z.array(zExpandOptions).optional().default([]),
22-
connector_name: z.string().optional(),
28+
connector_name: zConnectorName.optional(),
2329
})
2430
.optional(),
2531
)

packages-v1/api-v1/trpc/routers/event.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ export const eventRouter = router({
2525
// }),
2626
listEvents: publicProcedure
2727
.meta({
28-
openapi: {method: 'GET', path: '/event'},
28+
openapi: {
29+
method: 'GET',
30+
path: '/event',
31+
description: 'List all events for an organization',
32+
},
2933
})
3034
.input(zListParams.optional())
3135
.output(zListResponse(core.event))

packages-v1/api-v1/trpc/routers/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {z} from 'zod'
22
import {zViewer} from '@openint/cdk'
33
import {publicProcedure, router, trpc} from '../_base'
4+
import {connectRouter} from './connect'
45
import {connectionRouter} from './connection'
56
import {connectorConfigRouter} from './connectorConfig'
67
import {eventRouter} from './event'
@@ -23,6 +24,7 @@ export const appRouter = trpc.mergeRouters(
2324
connectorConfigRouter,
2425
eventRouter,
2526
generalRouter,
27+
connectRouter,
2628
)
2729

2830
export type AppRouter = typeof appRouter

packages/engine-backend/router/customerRouter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export const customerRouterSchema = {
127127

128128
// MARK: - Helpers
129129

130-
function asCustomer(
130+
export function asCustomer(
131131
viewer: Viewer,
132132
input: {customerId?: CustomerId | null},
133133
): Viewer<'customer'> {

0 commit comments

Comments
 (0)