Skip to content

Commit 767e09e

Browse files
authored
New integration: @trigger.dev/supabase (triggerdotdev#203)
* WIP supabase integration * supabase oauth working * Supabase database triggers * Specify postgres:14 * Limit refreshOAuthToken jobs to 10 attempts * Better displaying types and removing onChange for now * WIP on the supabase db client * Finishing the supabase-js integration * Adding changeset * Added supabase to the integration catalogs, and added an optional icon to Integrations * Reworking how we handle types for the triggers (wip) * Update fully over to the new way to define supabase triggers * Go back to using the type for the event name * Add back in the icon to the JobListPresenter since it was moved from the ProjectPresenter * Remove unused import
1 parent 8f050b4 commit 767e09e

File tree

45 files changed

+2004
-190
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2004
-190
lines changed

.changeset/wise-shirts-visit.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@trigger.dev/supabase": patch
3+
"@trigger.dev/sdk": patch
4+
---
5+
6+
Added io.integration.runTask and initial @trigger.dev/supabase integration

apps/webapp/app/components/integrations/ConnectToIntegrationSheet.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ export function ConnectToIntegrationSheet({
2828
button,
2929
className,
3030
callbackUrl,
31+
icon,
3132
}: {
3233
integration: Integration;
3334
organizationId: string;
3435
button: React.ReactNode;
3536
callbackUrl: string;
3637
className?: string;
38+
icon?: string;
3739
}) {
3840
const [integrationMethod, setIntegrationMethod] = useState<
3941
IntegrationMethod | undefined
@@ -48,7 +50,10 @@ export function ConnectToIntegrationSheet({
4850
<SheetTrigger className={className}>{button}</SheetTrigger>
4951
<SheetContent size="lg" className="relative">
5052
<SheetHeader>
51-
<NamedIconInBox name={integration.identifier} className="h-9 w-9" />
53+
<NamedIconInBox
54+
name={icon ?? integration.identifier}
55+
className="h-9 w-9"
56+
/>
5257
<div className="grow">
5358
<Header1>{integration.name}</Header1>
5459
{integration.description && (

apps/webapp/app/components/integrations/ConnectToOAuthForm.tsx

Lines changed: 67 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function ConnectToOAuthForm({
6060
},
6161
] = useForm({
6262
lastSubmission: fetcher.data,
63+
shouldRevalidate: "onSubmit",
6364
onValidate({ formData }) {
6465
return parse(formData, {
6566
// Create the schema without any constraint defined
@@ -187,15 +188,16 @@ export function ConnectToOAuthForm({
187188
</div>
188189
)}
189190
</div>
190-
<div>
191-
<Header2>Scopes</Header2>
192-
<Paragraph variant="small" className="mb-4">
193-
Select the scopes you want to grant to {integration.name} in order
194-
for it to access your data. Note: If you try and perform an action
195-
in a Job that requires a scope you haven’t granted, that task will
196-
fail.
197-
</Paragraph>
198-
{/* <Header3 className="mb-2">
191+
{authMethod.scopes.length > 0 && (
192+
<div>
193+
<Header2>Scopes</Header2>
194+
<Paragraph variant="small" className="mb-4">
195+
Select the scopes you want to grant to {integration.name} in order
196+
for it to access your data. Note: If you try and perform an action
197+
in a Job that requires a scope you haven’t granted, that task will
198+
fail.
199+
</Paragraph>
200+
{/* <Header3 className="mb-2">
199201
Select from popular scope collections
200202
</Header3>
201203
<fieldset>
@@ -205,60 +207,63 @@ export function ConnectToOAuthForm({
205207
variant="button/small"
206208
/>
207209
</fieldset> */}
208-
<div className="mb-2 mt-4 flex items-center justify-between">
209-
<Header3>Select {integration.name} scopes</Header3>
210-
<Paragraph variant="small" className="text-slate-500">
211-
{simplur`${selectedScopes.size} scope[|s] selected`}
212-
</Paragraph>
213-
</div>
214-
<Input
215-
placeholder="Search scopes"
216-
className="mb-2"
217-
variant="medium"
218-
icon="search"
219-
fullWidth={true}
220-
value={filterText}
221-
onChange={(e) => setFilterText(e.target.value)}
222-
/>
223-
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
224-
{filteredItems.length === 0 && (
225-
<Paragraph variant="small" className="p-4">
226-
No scopes match {filterText}. Try a different search query.
210+
<div className="mb-2 mt-4 flex items-center justify-between">
211+
<Header3>Select {integration.name} scopes</Header3>
212+
<Paragraph variant="small" className="text-slate-500">
213+
{simplur`${selectedScopes.size} scope[|s] selected`}
227214
</Paragraph>
228-
)}
229-
{authMethod.scopes.map((s) => {
230-
return (
231-
<Checkbox
232-
key={s.name}
233-
id={s.name}
234-
value={s.name}
235-
name="scopes"
236-
label={s.name}
237-
defaultChecked={s.defaultChecked ?? false}
238-
badges={s.annotations?.map((a) => a.label)}
239-
description={s.description}
240-
variant="description"
241-
className={cn(
242-
filteredItems.find((f) => f.name === s.name) ? "" : "hidden"
243-
)}
244-
onChange={(isChecked) => {
245-
if (isChecked) {
246-
setSelectedScopes((selected) => {
247-
selected.add(s.name);
248-
return new Set(selected);
249-
});
250-
} else {
251-
setSelectedScopes((selected) => {
252-
selected.delete(s.name);
253-
return new Set(selected);
254-
});
255-
}
256-
}}
257-
/>
258-
);
259-
})}
215+
</div>
216+
<Input
217+
placeholder="Search scopes"
218+
className="mb-2"
219+
variant="medium"
220+
icon="search"
221+
fullWidth={true}
222+
value={filterText}
223+
onChange={(e) => setFilterText(e.target.value)}
224+
/>
225+
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
226+
{filteredItems.length === 0 && (
227+
<Paragraph variant="small" className="p-4">
228+
No scopes match {filterText}. Try a different search query.
229+
</Paragraph>
230+
)}
231+
{authMethod.scopes.map((s) => {
232+
return (
233+
<Checkbox
234+
key={s.name}
235+
id={s.name}
236+
value={s.name}
237+
name="scopes"
238+
label={s.name}
239+
defaultChecked={s.defaultChecked ?? false}
240+
badges={s.annotations?.map((a) => a.label)}
241+
description={s.description}
242+
variant="description"
243+
className={cn(
244+
filteredItems.find((f) => f.name === s.name)
245+
? ""
246+
: "hidden"
247+
)}
248+
onChange={(isChecked) => {
249+
if (isChecked) {
250+
setSelectedScopes((selected) => {
251+
selected.add(s.name);
252+
return new Set(selected);
253+
});
254+
} else {
255+
setSelectedScopes((selected) => {
256+
selected.delete(s.name);
257+
return new Set(selected);
258+
});
259+
}
260+
}}
261+
/>
262+
);
263+
})}
264+
</div>
260265
</div>
261-
</div>
266+
)}
262267
</Fieldset>
263268

264269
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">
@@ -268,7 +273,7 @@ export function ConnectToOAuthForm({
268273
className="flex gap-2"
269274
disabled={transition.state !== "idle"}
270275
variant="primary/medium"
271-
LeadingIcon={integration.identifier}
276+
LeadingIcon={integration.icon ?? integration.identifier}
272277
>
273278
Connect to {integration.name}
274279
</Button>

apps/webapp/app/components/integrations/UpdateOAuthForm.tsx

Lines changed: 65 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,16 @@ export function UpdateOAuthForm({
172172
</div>
173173
)}
174174
</div>
175-
<div>
176-
<Header2>Scopes</Header2>
177-
<Paragraph variant="small" className="mb-4">
178-
Select the scopes you want to grant to {integration.name} in order
179-
for it to access your data. Note: If you try and perform an action
180-
in a Job that requires a scope you haven’t granted, that task will
181-
fail.
182-
</Paragraph>
183-
{/* <Header3 className="mb-2">
175+
{authMethod.scopes.length > 0 && (
176+
<div>
177+
<Header2>Scopes</Header2>
178+
<Paragraph variant="small" className="mb-4">
179+
Select the scopes you want to grant to {integration.name} in order
180+
for it to access your data. Note: If you try and perform an action
181+
in a Job that requires a scope you haven’t granted, that task will
182+
fail.
183+
</Paragraph>
184+
{/* <Header3 className="mb-2">
184185
Select from popular scope collections
185186
</Header3>
186187
<fieldset>
@@ -190,60 +191,63 @@ export function UpdateOAuthForm({
190191
variant="button/small"
191192
/>
192193
</fieldset> */}
193-
<div className="mb-2 mt-4 flex items-center justify-between">
194-
<Header3>Select {integration.name} scopes</Header3>
195-
<Paragraph variant="small" className="text-slate-500">
196-
{simplur`${selectedScopes.size} scope[|s] selected`}
197-
</Paragraph>
198-
</div>
199-
<Input
200-
placeholder="Search scopes"
201-
className="mb-2"
202-
variant="medium"
203-
icon="search"
204-
fullWidth={true}
205-
value={filterText}
206-
onChange={(e) => setFilterText(e.target.value)}
207-
/>
208-
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
209-
{filteredItems.length === 0 && (
210-
<Paragraph variant="small" className="p-4">
211-
No scopes match {filterText}. Try a different search query.
194+
<div className="mb-2 mt-4 flex items-center justify-between">
195+
<Header3>Select {integration.name} scopes</Header3>
196+
<Paragraph variant="small" className="text-slate-500">
197+
{simplur`${selectedScopes.size} scope[|s] selected`}
212198
</Paragraph>
213-
)}
214-
{authMethod.scopes.map((s) => {
215-
return (
216-
<Checkbox
217-
key={s.name}
218-
id={s.name}
219-
value={s.name}
220-
name="scopes"
221-
label={s.name}
222-
defaultChecked={s.defaultChecked ?? false}
223-
badges={s.annotations?.map((a) => a.label)}
224-
description={s.description}
225-
variant="description"
226-
className={cn(
227-
filteredItems.find((f) => f.name === s.name) ? "" : "hidden"
228-
)}
229-
onChange={(isChecked) => {
230-
if (isChecked) {
231-
setSelectedScopes((selected) => {
232-
selected.add(s.name);
233-
return new Set(selected);
234-
});
235-
} else {
236-
setSelectedScopes((selected) => {
237-
selected.delete(s.name);
238-
return new Set(selected);
239-
});
240-
}
241-
}}
242-
/>
243-
);
244-
})}
199+
</div>
200+
<Input
201+
placeholder="Search scopes"
202+
className="mb-2"
203+
variant="medium"
204+
icon="search"
205+
fullWidth={true}
206+
value={filterText}
207+
onChange={(e) => setFilterText(e.target.value)}
208+
/>
209+
<div className="mb-28 flex flex-col gap-y-0.5 overflow-hidden rounded-md">
210+
{filteredItems.length === 0 && (
211+
<Paragraph variant="small" className="p-4">
212+
No scopes match {filterText}. Try a different search query.
213+
</Paragraph>
214+
)}
215+
{authMethod.scopes.map((s) => {
216+
return (
217+
<Checkbox
218+
key={s.name}
219+
id={s.name}
220+
value={s.name}
221+
name="scopes"
222+
label={s.name}
223+
defaultChecked={s.defaultChecked ?? false}
224+
badges={s.annotations?.map((a) => a.label)}
225+
description={s.description}
226+
variant="description"
227+
className={cn(
228+
filteredItems.find((f) => f.name === s.name)
229+
? ""
230+
: "hidden"
231+
)}
232+
onChange={(isChecked) => {
233+
if (isChecked) {
234+
setSelectedScopes((selected) => {
235+
selected.add(s.name);
236+
return new Set(selected);
237+
});
238+
} else {
239+
setSelectedScopes((selected) => {
240+
selected.delete(s.name);
241+
return new Set(selected);
242+
});
243+
}
244+
}}
245+
/>
246+
);
247+
})}
248+
</div>
245249
</div>
246-
</div>
250+
)}
247251
</Fieldset>
248252

249253
<div className="absolute bottom-0 left-0 flex w-full items-center justify-end gap-x-4 rounded-b-md border-t border-slate-800 bg-midnight-900 p-4">

apps/webapp/app/components/run/TaskCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ export function TaskCard({
124124
)}
125125
{connection && (
126126
<RunPanelIconProperty
127-
icon={connection.integration.definitionId}
127+
icon={
128+
connection.integration.definition.icon ??
129+
connection.integration.definitionId
130+
}
128131
label="Connection"
129132
value={connection.integration.slug}
130133
/>

apps/webapp/app/presenters/IntegrationClientPresenter.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class IntegrationClientPresenter {
4848
id: true,
4949
name: true,
5050
packageName: true,
51+
icon: true,
5152
},
5253
},
5354
connectionType: true,
@@ -119,6 +120,7 @@ export class IntegrationClientPresenter {
119120
identifier: integration.definition.id,
120121
name: integration.definition.name,
121122
packageName: integration.definition.packageName,
123+
icon: integration.definition.icon,
122124
},
123125
authMethod: {
124126
type: integration.authMethod?.type ?? "local",

apps/webapp/app/presenters/IntegrationsPresenter.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class IntegrationsPresenter {
5151
select: {
5252
id: true,
5353
name: true,
54+
icon: true,
5455
},
5556
},
5657
authSource: true,
@@ -111,6 +112,7 @@ export class IntegrationsPresenter {
111112
return {
112113
id: c.id,
113114
title: c.title ?? c.slug,
115+
icon: c.definition.icon ?? c.definition.id,
114116
slug: c.slug,
115117
integrationIdentifier: c.definition.id,
116118
description: c.description,

apps/webapp/app/presenters/JobListPresenter.server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ export class JobListPresenter {
158158
const integrations = alias.version.integrations.map((integration) => ({
159159
key: integration.key,
160160
title: integration.integration.slug,
161-
icon: integration.integration.definition.id,
161+
icon:
162+
integration.integration.definition.icon ??
163+
integration.integration.definition.id,
162164
setupStatus: integration.integration.setupStatus,
163165
}));
164166

0 commit comments

Comments
 (0)