Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
dd78684
Removed curly bracket from the end of the combined schema
matt-aitken Feb 28, 2023
c6a668a
Set is constant
matt-aitken Feb 28, 2023
fa7ec12
Added a Product Hunt banner
samejr Mar 1, 2023
79f21fd
Fix for bad documentation url in Notion spec
matt-aitken Mar 1, 2023
923d2e7
Added a todo to throw an error on an edge case when registering a web…
matt-aitken Mar 1, 2023
3de6a9a
Moved HTTPMethod to a shared file
matt-aitken Mar 2, 2023
1296226
Started thinking about Webhook types
matt-aitken Mar 2, 2023
5a1e770
You can have an endpoint parameter that goes in the body
matt-aitken Mar 2, 2023
30e9714
WIP on Webhook types
matt-aitken Mar 2, 2023
a60d644
Experimenting with Typeform webhook data
matt-aitken Mar 2, 2023
77482a6
Updated combine schema tests
matt-aitken Mar 2, 2023
9642d4b
Added JSONPointer
matt-aitken Mar 2, 2023
3c02cf7
Added the ability for an endpoint to have body params and static body…
matt-aitken Mar 2, 2023
470b024
Progress on webhook specs and making a webhook
matt-aitken Mar 2, 2023
746e655
Subscribing to webhook working
matt-aitken Mar 2, 2023
bdfff2c
Subscribing to webhooks now allows manipulating the data after the re…
matt-aitken Mar 2, 2023
8ac8042
Lots more progress on webhook and event types/functions
matt-aitken Mar 2, 2023
3d1adfd
Fixed the typeform webhook so the test passes again
matt-aitken Mar 2, 2023
392307b
Started fleshing out the first event
matt-aitken Mar 2, 2023
9f9e816
Webhooks don’t need displayProperties, they’re never going to be disp…
matt-aitken Mar 3, 2023
de1508f
Typeform schema and receiving test working
matt-aitken Mar 3, 2023
df7d6a7
Deal with signed webhook
matt-aitken Mar 3, 2023
bdaa090
Don’t mess with the secret for Typeform as it doesn’t need it
matt-aitken Mar 3, 2023
cb5128c
Moved the db.server.ts file so it can be used outside of the cache
matt-aitken Mar 3, 2023
d09b5fe
Renamed preEvent to preProcess
matt-aitken Mar 3, 2023
f474621
Setup objects ready to subscribe to webhooks
matt-aitken Mar 3, 2023
dc805e1
service.actions is now optional
matt-aitken Mar 3, 2023
91632bc
Added webhooks to service
matt-aitken Mar 3, 2023
a8fbdb1
Started work on subscribing from an input
matt-aitken Mar 3, 2023
92c3409
Service webhooks now support manual and automatic subscription types
matt-aitken Mar 3, 2023
d6dcb4a
Pass the webhookId into subscribe so it can be used in the subscripti…
matt-aitken Mar 3, 2023
e1edccd
Defined the webhook table schema
matt-aitken Mar 3, 2023
08abe07
WIP on registration endpoint
matt-aitken Mar 3, 2023
b616454
Manual webhook subscription progress
matt-aitken Mar 3, 2023
ab20846
Credentials, automatic webhooks, a lot
matt-aitken Mar 3, 2023
c064fb9
Delete unused file
matt-aitken Mar 3, 2023
ace0999
Added Typeform to the catalog
matt-aitken Mar 3, 2023
087156d
Nango is now imported dynamically
matt-aitken Mar 3, 2023
f2ced19
Removed webhooks from the services API endpoint return
matt-aitken Mar 3, 2023
57bbbba
Registering a webhook is working and almost creating all correct db e…
matt-aitken Mar 3, 2023
06b63e6
API key webhook is registering correctly
matt-aitken Mar 3, 2023
a04ceb4
Added support for rawBody
matt-aitken Mar 4, 2023
fc504ea
Changed the env var to WEBHOOKS_ORIGIN to make it less confusing
matt-aitken Mar 4, 2023
d06a1ea
Stub for receiving webhook
matt-aitken Mar 4, 2023
04ad242
Started receiving webhooks endpoint
matt-aitken Mar 4, 2023
3c5a388
Lots of receiving webhook work
matt-aitken Mar 4, 2023
aafe9c7
Create webhook deliveries and add Graphile worker jobs
matt-aitken Mar 4, 2023
fbcc214
Sending request to the webhook destination
matt-aitken Mar 4, 2023
1cc394e
When subscribing, send the destination secret back so we can check th…
matt-aitken Mar 4, 2023
683c40a
Created the endpoint for receiving events in the web app
matt-aitken Mar 4, 2023
5159ee4
Added displayProperties to TriggerEvent and the integrations side too
matt-aitken Mar 4, 2023
6760c72
Added event column to ExternalSource
matt-aitken Mar 4, 2023
b92b82e
Added subscribe functionality for the INTEGRATION_WEBHOOK
matt-aitken Mar 4, 2023
29c5a94
Key that gets passed through and added registering logic
matt-aitken Mar 5, 2023
7477fed
Generating the SDK with the events in
matt-aitken Mar 5, 2023
46a426c
Fix for typecheck issue
matt-aitken Mar 5, 2023
98e05ac
Code generation working
matt-aitken Mar 5, 2023
2a92afd
Created a typeform example
matt-aitken Mar 5, 2023
ebb6002
Add the key and event to the external source when it’s an integration…
matt-aitken Mar 5, 2023
303558e
Turn auth off for the webhook endpoint
matt-aitken Mar 5, 2023
22ceeb5
Fix for the webhook url
matt-aitken Mar 5, 2023
dba572f
Added ngrok for integrations
matt-aitken Mar 5, 2023
3887593
Updated to a different Typeform id
matt-aitken Mar 5, 2023
e26ac49
Workflows are receiving the new webhooks
matt-aitken Mar 5, 2023
ecbcc20
Created Typeform schema tests and fixed them
matt-aitken Mar 5, 2023
befa80f
New generated Typeform SDK
matt-aitken Mar 5, 2023
44c424b
Added organization the DispatchEvent log
matt-aitken Mar 6, 2023
84b276f
Changed the example form to a new one
matt-aitken Mar 6, 2023
07fb86e
hello-world repo
matt-aitken Mar 6, 2023
8136e43
Added INTEGRATION_WEBHOOK type label
matt-aitken Mar 6, 2023
118b257
Improved the display of the INTEGRATION_WEBHOOK
matt-aitken Mar 6, 2023
923d572
Much better Typeform icon
matt-aitken Mar 6, 2023
6c913e8
Added displayProperties to external source and display them
matt-aitken Mar 6, 2023
449c40e
Updated the example form
matt-aitken Mar 6, 2023
05592e8
Added changeset for the sdk and typeform packages
matt-aitken Mar 6, 2023
1558b9e
Added instructions and examples
matt-aitken Mar 6, 2023
5e5dff3
Fix for type error
matt-aitken Mar 6, 2023
0b35f5f
inputSchema isn’t required for a webhook
matt-aitken Mar 7, 2023
41a36e7
Removed logs from tests
matt-aitken Mar 7, 2023
06a9ce7
WIP on form-urlencoded bodies… annoying
matt-aitken Mar 7, 2023
55f7ca0
Moe WIP on form urlencoding… this is a pain
matt-aitken Mar 7, 2023
203b1bc
Stripe webhook subscribing is working
matt-aitken Mar 7, 2023
c65ab2a
Work on the event schema
matt-aitken Mar 7, 2023
87b347f
Open API Specs with “nullable” in can be coerced into valid JSON schemas
matt-aitken Mar 8, 2023
ece090e
Support for refing into a schema
matt-aitken Mar 8, 2023
7ae0719
Removed fetch log
matt-aitken Mar 8, 2023
dddfc6a
Stripe event schema and reffing to it
matt-aitken Mar 8, 2023
857b45b
Added Stripe to the catalog
matt-aitken Mar 8, 2023
8e6b2ed
Metadata setup for the checkout event
matt-aitken Mar 8, 2023
fd9578e
WIP creating a schema from a ref
matt-aitken Mar 8, 2023
7395d42
Generate a flat schema from a ref into a spec
matt-aitken Mar 8, 2023
ce579a3
Improved the dereffer, but it’s still too recursive
matt-aitken Mar 8, 2023
fcb80c2
Removed periods from type names
matt-aitken Mar 8, 2023
09ce655
Deal with no params for webhooks
matt-aitken Mar 8, 2023
84720ce
Non recursive flat schema generator
matt-aitken Mar 8, 2023
08a1198
Combine schemas and hoist references so they still work
matt-aitken Mar 8, 2023
ee7a7a5
Set the ref if we change it
matt-aitken Mar 8, 2023
b44a350
First decent schema generated for Stripe
matt-aitken Mar 8, 2023
5290be7
Verifying Stripe webhooks is working and they’re coming through
matt-aitken Mar 8, 2023
a20f5ec
Stripe event for checkout.session.completed is working
matt-aitken Mar 8, 2023
c4d5ec9
Moved some Stripe folders to keep things tidier as we do more events
matt-aitken Mar 9, 2023
fe193be
Removed commented out tests
matt-aitken Mar 9, 2023
30fd308
Added more Stripe events
matt-aitken Mar 9, 2023
b656398
Added more examples of Stripe
matt-aitken Mar 9, 2023
771b77c
Use refs and a centralized schema for an integration with definitions
matt-aitken Mar 14, 2023
62ff110
Improved the code generation
matt-aitken Mar 14, 2023
d119451
Removed old schema code
matt-aitken Mar 14, 2023
9584876
Updated the Typeform integration with refs
matt-aitken Mar 14, 2023
fe790aa
Stripe webhook endpoint specs
matt-aitken Mar 14, 2023
ba4b4fa
Don’t generate a schema json file when creating the integration
matt-aitken Mar 14, 2023
746e768
If there’s no input or output then don’t include that in the generate…
matt-aitken Mar 14, 2023
e7b9fff
SendGrid schemas
matt-aitken Mar 14, 2023
48dc45a
Notion now uses $refs
matt-aitken Mar 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-needles-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---

Added support for INTEGRATION_WEBHOOK trigger type
5 changes: 5 additions & 0 deletions .changeset/serious-maps-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/typeform": patch
---

First release of the Typeform integration, with webhook support
13 changes: 12 additions & 1 deletion apps/integrations/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,43 @@
"lint-fix": "eslint --no-error-on-unmatched-pattern --fix src/**",
"generate-integrations": "NODE_OPTIONS=--max-old-space-size=16384 tsx src/trigger/sdk/generate.ts generate",
"generate": "prisma generate",
"convert": "tsx src/tools/schemas/typescript-to-json-schema.ts convert",
"convert-ts": "tsx src/tools/schemas/typescript-to-json-schema.ts convert",
"convert-3": "tsx src/tools/schemas/openapi-3-to-3.1.ts convert",
"db:migrate:deploy": "prisma migrate deploy",
"db:migrate:dev": "prisma migrate dev"
},
"dependencies": {
"@cfworker/json-schema": "^1.12.5",
"@nangohq/node": "^0.8.4",
"@prisma/client": "^4.3.0",
"@sentry/node": "^7.38.0",
"ajv": "^8.12.0",
"body-parser": "^1.20.2",
"express": "^4.18.1",
"express-async-errors": "^3.1.1",
"graphile-worker": "^0.13.0",
"json-pointer": "^0.6.2",
"json-schema": "^0.4.0",
"json-schema-deref-sync": "^0.14.0",
"json-schema-to-typescript": "^11.0.3",
"loglevel": "^1.8.1",
"morgan": "^1.10.0",
"node-fetch": "^3.3.0",
"node-object-hash": "^2.3.10",
"qs": "^6.11.0",
"stripe": "^11.13.0",
"zod": "^3.20.2"
},
"devDependencies": {
"@apidevtools/json-schema-ref-parser": "^10.1.0",
"@types/body-parser": "^1.19.2",
"@types/eslint": "^8.4.6",
"@types/express": "^4.17.13",
"@types/json-pointer": "^1.0.31",
"@types/json-schema": "^7.0.11",
"@types/morgan": "^1.9.3",
"@types/node": "^18.13.0",
"@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"commander": "^9.4.1",
"concurrently": "^7.6.0",
Expand All @@ -51,6 +61,7 @@
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.6.1",
"eslint-plugin-promise": "^6.1.1",
"json-schema-to-zod": "^0.6.3",
"nock": "^13.3.0",
"nodemon": "^2.0.19",
"prisma": "^4.3.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- CreateEnum
CREATE TYPE "WebhookType" AS ENUM ('SERVICE', 'GENERIC');

-- CreateEnum
CREATE TYPE "WebhookStatus" AS ENUM ('CREATED', 'READY', 'CANCELLED');

-- CreateEnum
CREATE TYPE "WebhookSubscriptionType" AS ENUM ('AUTOMATIC', 'MANUAL');

-- CreateTable
CREATE TABLE "Webhook" (
"id" TEXT NOT NULL,
"type" "WebhookType" NOT NULL,
"status" "WebhookStatus" NOT NULL,
"consumerId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"secret" TEXT,
"subscriptionType" "WebhookSubscriptionType" NOT NULL,
"externalData" JSONB,
"service" TEXT,
"webhookName" TEXT,
"authenticationData" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "Webhook_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Webhook_consumerId_key_key" ON "Webhook"("consumerId", "key");
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- CreateTable
CREATE TABLE "Destination" (
"id" TEXT NOT NULL,
"webhookId" TEXT NOT NULL,
"destinationUrl" TEXT NOT NULL,
"destinationSecret" TEXT NOT NULL,
"destinationData" JSONB,
"destinationEvent" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "Destination_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Destination_destinationUrl_key" ON "Destination"("destinationUrl");

-- AddForeignKey
ALTER TABLE "Destination" ADD CONSTRAINT "Destination_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- Added the required column `url` to the `Webhook` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Webhook" ADD COLUMN "url" TEXT NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- You are about to drop the column `url` on the `Webhook` table. All the data in the column will be lost.

*/
-- AlterTable
ALTER TABLE "Webhook" DROP COLUMN "url";
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- CreateTable
CREATE TABLE "WebhookEventDelivery" (
"id" TEXT NOT NULL,
"eventName" TEXT NOT NULL,
"destinationId" TEXT NOT NULL,
"payload" JSONB NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "WebhookEventDelivery_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "WebhookEventDelivery" ADD CONSTRAINT "WebhookEventDelivery_destinationId_fkey" FOREIGN KEY ("destinationId") REFERENCES "Destination"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:

- Added the required column `displayProperties` to the `WebhookEventDelivery` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "WebhookEventDelivery" ADD COLUMN "displayProperties" JSONB NOT NULL;
71 changes: 71 additions & 0 deletions apps/integrations/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,75 @@ model Cache {
createdAt DateTime @default(now())

@@unique([namespace, key])
}

model Webhook {
id String @id @default(cuid())
type WebhookType
status WebhookStatus
consumerId String
key String
secret String?
subscriptionType WebhookSubscriptionType
/// automatic subscriptions responses are stored here
externalData Json?

//service
service String?
webhookName String?
authenticationData Json?

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

destinations Destination[]

@@unique([consumerId, key])
}

enum WebhookType {
SERVICE
GENERIC
}

enum WebhookStatus {
CREATED
READY
CANCELLED
}

enum WebhookSubscriptionType {
AUTOMATIC
MANUAL
}

model Destination {
id String @id @default(cuid())

webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade, onUpdate: Cascade)
webhookId String

destinationUrl String @unique
destinationSecret String
destinationData Json?
destinationEvent String

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

deliveries WebhookEventDelivery[]
}

model WebhookEventDelivery {
id String @id @default(cuid())
eventName String

destination Destination @relation(fields: [destinationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
destinationId String

displayProperties Json
payload Json

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
12 changes: 12 additions & 0 deletions apps/integrations/src/api/v2/action/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ export function getServiceAction(
};
}

if (!matchingService.actions) {
return {
success: false,
error: {
type: "missing_action",
message: "Action not found",
service,
action,
},
};
}

const matchingAction = Object.values(matchingService.actions).find(
(a) => a.name === action
);
Expand Down
2 changes: 1 addition & 1 deletion apps/integrations/src/api/v2/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AuthCredentials } from "core/authentication/types";
import { HTTPMethodSchema } from "core/endpoint/types";
import { serviceFetch } from "core/fetch/serviceFetch";
import { HTTPMethodSchema } from "core/request/types";
import { Request, Response } from "express";
import { catalog } from "integrations/catalog";
import { z } from "zod";
Expand Down
4 changes: 2 additions & 2 deletions apps/integrations/src/api/v2/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function handleServices(_req: Request, res: Response) {

function omitExtraInfo(
service: Service
): Omit<Service, "actions" | "retryableStatusCodes"> {
const { actions, retryableStatusCodes, ...rest } = service;
): Omit<Service, "actions" | "retryableStatusCodes" | "webhooks"> {
const { actions, retryableStatusCodes, webhooks, ...rest } = service;
return rest;
}
51 changes: 51 additions & 0 deletions apps/integrations/src/api/v2/webhooks/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Request, Response } from "express";
import {
SubscribeInputSchema,
SubscribeResult,
} from "core/webhook/subscribe/types";
import { SubscribeToWebhook } from "core/webhook/subscribe";
import { Prisma } from "db/db.server";

export async function handleCreateWebhook(req: Request, res: Response) {
const parsedBody = SubscribeInputSchema.safeParse(req.body);

if (!parsedBody.success) {
const badBodyResponse: SubscribeResult = {
success: false,
error: {
code: "bad_body",
message: parsedBody.error.toString(),
},
};
res.status(400).json(badBodyResponse);
return;
}

try {
const subscribeToWebhook = new SubscribeToWebhook();
const result = await subscribeToWebhook.call(parsedBody.data);

if (result.success) {
res.status(201).json(result);
return;
}

res.status(400).json(result);
return;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === "P2002") {
res.status(400).json({
success: false,
error: {
code: "duplicate_webhook",
message: `A webhook with this ${error.meta?.target} already exists`,
},
});
return;
}
}

res.status(500).json(error);
}
}
47 changes: 47 additions & 0 deletions apps/integrations/src/api/v2/webhooks/receive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createDeliveriesAndTasks } from "core/jobs/tasks/webhookJob";
import { HTTPMethod } from "core/request/types";
import { ReceiveWebhook } from "core/webhook/receive";
import {
WebhookIncomingRequest,
WebhookReceiveRequest,
} from "core/webhook/types";
import { Request, Response } from "express";

export async function handleReceivingWebhook(req: Request, res: Response) {
const { webhookId } = req.params;

if (!webhookId) {
res.status(400).json({ success: false, error: "Missing webhook ID" });
return;
}

const headers = Object.entries(req.headers)
.filter(([key, value]) => value !== undefined)
.reduce((acc, [key, value]) => {
acc[key] =
typeof value === "string" ? (value as string) : value?.join(", ") ?? "";
return acc;
}, {} as Record<string, string>);

const request: WebhookIncomingRequest = {
method: req.method as HTTPMethod,
searchParams: new URLSearchParams(req.url),
headers,
body: req.body,
rawBody: req.rawBody,
};

try {
const receiver = new ReceiveWebhook();
const result = await receiver.call({ request, webhookId });

const response = res.status(result.response.status);
Object.entries(result.response.headers).forEach(([key, value]) => {
response.header(key, value);
});
response.json(result.response.body);
} catch (error) {
console.error(error);
res.status(500).json({ success: false, error: "Internal server error" });
}
}
3 changes: 1 addition & 2 deletions apps/integrations/src/cache/postgresCache.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Prisma, Cache } from ".prisma/client";
import { prisma } from "./db.server";
import { CacheService } from "core/cache/types";
import { prisma } from "../db/db.server";

export class PostgresCacheService implements CacheService {
constructor(private readonly namespace: string) {}
Expand Down
2 changes: 1 addition & 1 deletion apps/integrations/src/core/action/makeAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CacheService } from "core/cache/types";
import { Endpoint } from "core/endpoint/types";
import { RequestData, RequestResponse } from "core/request/types";
import { getDisplayProperties } from "./getDisplayProperties";
import { Action, InputSpec, Metadata, OutputSpec } from "./types";
import { Action, Metadata } from "./types";
import { makeInputSpec, makeOutputSpec } from "./utilities";

/** Create an action where you specify the spec and action */
Expand Down
Loading