Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 1 addition & 1 deletion components/notion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipedream/notion",
"version": "0.9.1",
"version": "0.10.0",
"description": "Pipedream Notion Components",
"main": "notion.app.mjs",
"keywords": [
Expand Down
56 changes: 56 additions & 0 deletions components/notion/sources/common/base-webhook.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import notion from "../../notion.app.mjs";
import {
createHmac, timingSafeEqual,
} from "crypto";
import { ConfigurationError } from "@pipedream/platform";

export default {
props: {
notion,
db: "$.service.db",
http: "$.interface.http",
info: {

Check warning on line 12 in components/notion/sources/common/base-webhook.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Component prop info must have a description. See https://pipedream.com/docs/components/guidelines/#props

Check warning on line 12 in components/notion/sources/common/base-webhook.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Component prop info must have a label. See https://pipedream.com/docs/components/guidelines/#props
type: "alert",
alertType: "info",
content: "Webhooks must be created and verified in Notion. [See the documentation](https://developers.notion.com/reference/webhooks#step-1-creating-a-webhook-subscription). Upon webhook creation, the source logs will display a verification token. Enter this in your Notion webhook settings.",
},
},
methods: {
_getToken() {
return this.db.get("token");
},
_setToken(token) {
this.db.set("token", token);
},
verifyWebhook(token, body, headers) {
const calculatedSignature = `sha256=${createHmac("sha256", token).update(JSON.stringify(body))
.digest("hex")}`;
return timingSafeEqual(
Buffer.from(calculatedSignature),
Buffer.from(headers["x-notion-signature"]),
);
},
processEvent() {
throw new ConfigurationError("processEvent must be implemented in the source");
},
},
async run(event) {
const {
body, headers,
} = event;
if (!body) {
return;
}
const token = this._getToken();
if (body.verification_token && !token) {
this._setToken(body.verification_token);
console.log(`Verification token: ${body.verification_token}. Enter this in your Notion webhook settings.`);
return;
}
if (!this.verifyWebhook(token, body, headers)) {
throw new Error("Invalid webhook signature");
}

await this.processEvent(body);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import common from "../common/base-webhook.mjs";
import sampleEmit from "./test-event.mjs";

export default {
...common,
key: "notion-new-webhook-event-instant",
name: "New Webhook Event (Instant)",
description: "Emit new event each time a webhook event is received. Webhook must be setup in Notion. [See the documentation](https://developers.notion.com/reference/webhooks#step-1-creating-a-webhook-subscription)",
version: "0.0.1",
type: "source",
dedupe: "unique",
methods: {
...common.methods,
_generateMeta(event) {
return {
id: event.id,
summary: `Webhook event: ${event.type}`,
ts: Date.now(),
};
},
processEvent(event) {
const meta = this._generateMeta(event);
this.$emit(event, meta);
},
},
sampleEmit,
};
31 changes: 31 additions & 0 deletions components/notion/sources/new-webhook-event-instant/test-event.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default {
"id": "555831bd-c1d6-400b-873e-42b10fb7f7e8",
"timestamp": "2025-08-27T20:51:19.356Z",
"workspace_id": "e0f732bd-e43f-4db3-b923-8a12ffdbfcce",
"workspace_name": "Notion",
"subscription_id": "25cd872b-594c-8181-982c-009998a96535",
"integration_id": "1ded872b-594c-804e-88d9-0037511f15c1",
"authors": [
{
"id": "22da95d5-0f61-4b51-963d-63c4cf51ae19",
"type": "person"
}
],
"attempt_number": 1,
"api_version": "2022-06-28",
"entity": {
"id": "10773a03-a25e-8061-8256-f26813633a59",
"type": "page"
},
"type": "page.properties_updated",
"data": {
"parent": {
"id": "9813bf8c-a429-4d63-b73b-f476783ff448",
"type": "database",
"data_source_id": "dabbe504-b5ff-48f1-a1fa-d5fea5ef9ac4"
},
"updated_properties": [
"title"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import common from "../common/base-webhook.mjs";
import sampleEmit from "./test-event.mjs";

export default {
...common,
key: "notion-page-properties-updated-instant",
name: "Page Properties Updated (Instant)",

Check warning on line 7 in components/notion/sources/page-properties-updated-instant/page-properties-updated-instant.mjs

View workflow job for this annotation

GitHub Actions / Lint Code Base

Source names should start with "New". See https://pipedream.com/docs/components/guidelines/#source-name
description: "Emit new event each time a page property is updated in a database. For use with Page Properties Updated event type. Webhook must be setup in Notion. [See the documentation](https://developers.notion.com/reference/webhooks#step-1-creating-a-webhook-subscription)",
version: "0.0.1",
type: "source",
dedupe: "unique",
props: {
...common.props,
databaseId: {
propDefinition: [
common.props.notion,
"databaseId",
],
},
properties: {
type: "string[]",
label: "Properties",
description: "Only emit events when one or more of the selected properties have changed",
optional: true,
async options() {
try {
const { properties } = await this.notion.retrieveDatabase(this.databaseId);
const propEntries = Object.entries(properties);
return propEntries.map((prop) => ({
label: prop[1].name,
value: prop[1].id,
}));
} catch (error) {
console.log(error);
return [];
}
},
},
},
methods: {
...common.methods,
_generateMeta(page) {
const { id } = page;
const title = this.notion.extractPageTitle(page);
const ts = Date.now();
return {
id: `${id}-${ts}`,
summary: `Page updated: ${title}`,
ts,
};
},
async processEvent(event) {
if (event?.type !== "page.properties_updated") {
console.log(`Skipping event type: ${event?.type}`);
return;
}

if (event.data.parent.id !== this.databaseId) {
console.log(`Skipping event for database: ${event.data.parent.id}`);
return;
}

const updatedProperties = event.data.updated_properties;
if (!updatedProperties?.length) {
return;
}

let propertyHasChanged = false;
for (const propertyName of updatedProperties) {
if (!this.properties || this.properties.includes(propertyName)) {
propertyHasChanged = true;
}
}

if (!propertyHasChanged) {
return;
}

const page = await this.notion.retrievePage(event.entity.id);

const meta = this._generateMeta(page);
this.$emit({
...event,
page,
}, meta);
},
},
sampleEmit,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export default {
"id": "1782edd6-a853-4d4a-b02c-9c8c16f28e53",
"timestamp": "2024-12-05T23:57:05.379Z",
"workspace_id": "13950b26-c203-4f3b-b97d-93ec06319565",
"workspace_name": "Quantify Labs",
"subscription_id": "29d75c0d-5546-4414-8459-7b7a92f1fc4b",
"integration_id": "0ef2e755-4912-8096-91c1-00376a88a5ca",
"type": "page.properties_updated",
"authors": [
{
"id": "c7c11cca-1d73-471d-9b6e-bdef51470190",
"type": "person"
}
],
"accessible_by": [
{
"id": "556a1abf-4f08-40c6-878a-75890d2a88ba",
"type": "person"
},
{
"id": "1edc05f6-2702-81b5-8408-00279347f034",
"type": "bot"
}
],
"attempt_number": 1,
"entity": {
"id": "153104cd-477e-809d-8dc4-ff2d96ae3090",
"type": "page"
},
"data": {
"parent": {
"id": "13950b26-c203-4f3b-b97d-93ec06319565",
"type": "space"
},
"updated_properties": ["XGe%40", "bDf%5B", "DbAu"]
}
}
5 changes: 3 additions & 2 deletions pnpm-lock.yaml

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

Loading