diff --git a/components/gmail/actions/add-label-to-email/add-label-to-email.mjs b/components/gmail/actions/add-label-to-email/add-label-to-email.mjs
index 80f56623f563d..d5d8c954226a5 100644
--- a/components/gmail/actions/add-label-to-email/add-label-to-email.mjs
+++ b/components/gmail/actions/add-label-to-email/add-label-to-email.mjs
@@ -4,7 +4,7 @@ export default {
key: "gmail-add-label-to-email",
name: "Add Label to Email",
description: "Add label(s) to an email message. [See the docs](https://developers.google.com/gmail/api/reference/rest/v1/users.messages/modify)",
- version: "0.0.4",
+ version: "0.0.5",
type: "action",
props: {
gmail,
diff --git a/components/gmail/actions/create-draft/create-draft.mjs b/components/gmail/actions/create-draft/create-draft.mjs
index 127cb3edee233..fb8cd9431fb78 100644
--- a/components/gmail/actions/create-draft/create-draft.mjs
+++ b/components/gmail/actions/create-draft/create-draft.mjs
@@ -1,10 +1,12 @@
import gmail from "../../gmail.app.mjs";
+import { ConfigurationError } from "@pipedream/platform";
+import utils from "../../common/utils.mjs";
export default {
key: "gmail-create-draft",
name: "Create Draft",
description: "Create a draft from your Google Workspace email account. [See the documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.drafts/create)",
- version: "0.0.2",
+ version: "0.0.3",
type: "action",
props: {
gmail,
@@ -44,10 +46,16 @@ export default {
"bodyType",
],
},
- attachments: {
+ attachmentFilenames: {
propDefinition: [
gmail,
- "attachments",
+ "attachmentFilenames",
+ ],
+ },
+ attachmentUrlsOrPaths: {
+ propDefinition: [
+ gmail,
+ "attachmentUrlsOrPaths",
],
},
inReplyTo: {
@@ -67,6 +75,11 @@ export default {
},
},
async run({ $ }) {
+ this.attachmentFilenames = utils.parseArray(this.attachmentFilenames);
+ this.attachmentUrlsOrPaths = utils.parseArray(this.attachmentUrlsOrPaths);
+ if (this.attachmentFilenames?.length !== this.attachmentUrlsOrPaths?.length) {
+ throw new ConfigurationError("Must specify the same number of `Attachment Filenames` and `Attachment URLs or Paths`");
+ }
const opts = await this.gmail.getOptionsToSendEmail($, this);
const response = await this.gmail.createDraft(opts);
$.export("$summary", "Successfully created a draft message");
diff --git a/components/gmail/actions/download-attachment/download-attachment.mjs b/components/gmail/actions/download-attachment/download-attachment.mjs
index 7f10f5a3edd59..d60c16559b8b8 100644
--- a/components/gmail/actions/download-attachment/download-attachment.mjs
+++ b/components/gmail/actions/download-attachment/download-attachment.mjs
@@ -7,7 +7,7 @@ export default {
key: "gmail-download-attachment",
name: "Download Attachment",
description: "Download an attachment by attachmentId to the /tmp directory. [See the documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.messages.attachments/get)",
- version: "0.0.2",
+ version: "0.0.3",
type: "action",
props: {
gmail,
diff --git a/components/gmail/actions/find-email/find-email.mjs b/components/gmail/actions/find-email/find-email.mjs
index 3f9acb74a6c7e..b5b1daa8615df 100644
--- a/components/gmail/actions/find-email/find-email.mjs
+++ b/components/gmail/actions/find-email/find-email.mjs
@@ -4,7 +4,7 @@ export default {
key: "gmail-find-email",
name: "Find Email",
description: "Find an email using Google's Search Engine. [See the docs](https://developers.google.com/gmail/api/reference/rest/v1/users.messages/list)",
- version: "0.0.5",
+ version: "0.0.6",
type: "action",
props: {
gmail,
diff --git a/components/gmail/actions/remove-label-from-email/remove-label-from-email.mjs b/components/gmail/actions/remove-label-from-email/remove-label-from-email.mjs
index dfa64f9777e08..7b7971028f79b 100644
--- a/components/gmail/actions/remove-label-from-email/remove-label-from-email.mjs
+++ b/components/gmail/actions/remove-label-from-email/remove-label-from-email.mjs
@@ -4,7 +4,7 @@ export default {
key: "gmail-remove-label-from-email",
name: "Remove Label from Email",
description: "Remove label(s) from an email message. [See the docs](https://developers.google.com/gmail/api/reference/rest/v1/users.messages/modify)",
- version: "0.0.1",
+ version: "0.0.2",
type: "action",
props: {
gmail,
diff --git a/components/gmail/actions/send-email/send-email.mjs b/components/gmail/actions/send-email/send-email.mjs
index 357dcc7e9ac66..07022a4077804 100644
--- a/components/gmail/actions/send-email/send-email.mjs
+++ b/components/gmail/actions/send-email/send-email.mjs
@@ -1,10 +1,12 @@
import gmail from "../../gmail.app.mjs";
+import { ConfigurationError } from "@pipedream/platform";
+import utils from "../../common/utils.mjs";
export default {
key: "gmail-send-email",
name: "Send Email",
description: "Send an email from your Google Workspace email account. [See the documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.messages/send)",
- version: "0.1.4",
+ version: "0.1.5",
type: "action",
props: {
gmail,
@@ -32,6 +34,12 @@ export default {
"fromName",
],
},
+ fromEmail: {
+ propDefinition: [
+ gmail,
+ "fromEmail",
+ ],
+ },
replyTo: {
propDefinition: [
gmail,
@@ -56,10 +64,16 @@ export default {
"bodyType",
],
},
- attachments: {
+ attachmentFilenames: {
+ propDefinition: [
+ gmail,
+ "attachmentFilenames",
+ ],
+ },
+ attachmentUrlsOrPaths: {
propDefinition: [
gmail,
- "attachments",
+ "attachmentUrlsOrPaths",
],
},
inReplyTo: {
@@ -76,6 +90,11 @@ export default {
},
},
async run({ $ }) {
+ this.attachmentFilenames = utils.parseArray(this.attachmentFilenames);
+ this.attachmentUrlsOrPaths = utils.parseArray(this.attachmentUrlsOrPaths);
+ if (this.attachmentFilenames?.length !== this.attachmentUrlsOrPaths?.length) {
+ throw new ConfigurationError("Must specify the same number of `Attachment Filenames` and `Attachment URLs or Paths`");
+ }
const opts = await this.gmail.getOptionsToSendEmail($, this);
const response = await this.gmail.sendEmail(opts);
$.export("$summary", `Successfully sent email to ${this.to}`);
diff --git a/components/gmail/actions/update-org-signature/update-org-signature.mjs b/components/gmail/actions/update-org-signature/update-org-signature.mjs
index 0eefa352ceadb..4b62e9fc094af 100644
--- a/components/gmail/actions/update-org-signature/update-org-signature.mjs
+++ b/components/gmail/actions/update-org-signature/update-org-signature.mjs
@@ -8,7 +8,7 @@ export default {
name: "Update Signature for Email in Organization",
description: `Update the signature for a specific email address in an organization.
A Google Cloud service account with delegated domain-wide authority is required for this action. [See the documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.settings.sendAs/update)`,
- version: "0.0.2",
+ version: "0.0.3",
type: "action",
props: {
gmail,
diff --git a/components/gmail/actions/update-primary-signature/update-primary-signature.mjs b/components/gmail/actions/update-primary-signature/update-primary-signature.mjs
index 17a8b6b6f74af..efe7739fb5002 100644
--- a/components/gmail/actions/update-primary-signature/update-primary-signature.mjs
+++ b/components/gmail/actions/update-primary-signature/update-primary-signature.mjs
@@ -4,7 +4,7 @@ export default {
key: "gmail-update-primary-signature",
name: "Update Signature for Primary Email Address",
description: "Update the signature for the primary email address. [See the documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.settings.sendAs/update)",
- version: "0.0.2",
+ version: "0.0.3",
type: "action",
props: {
gmail,
diff --git a/components/gmail/common/utils.mjs b/components/gmail/common/utils.mjs
new file mode 100644
index 0000000000000..ae408a74c6adb
--- /dev/null
+++ b/components/gmail/common/utils.mjs
@@ -0,0 +1,12 @@
+function parseArray(arr) {
+ if (!arr) {
+ return undefined;
+ }
+ return typeof arr === "string"
+ ? JSON.parse(arr)
+ : arr;
+}
+
+export default {
+ parseArray,
+};
diff --git a/components/gmail/gmail.app.mjs b/components/gmail/gmail.app.mjs
index 592faeb975c61..c8f3f875f7c62 100644
--- a/components/gmail/gmail.app.mjs
+++ b/components/gmail/gmail.app.mjs
@@ -195,6 +195,18 @@ export default {
description: "Specify the name that will be displayed in the \"From\" section of the email.",
optional: true,
},
+ fromEmail: {
+ type: "string",
+ label: "From Email",
+ description: "Specify the email address that will be displayed in the \"From\" section of the email.",
+ optional: true,
+ async options() {
+ const { sendAs } = await this.listSignatures();
+ return sendAs
+ .filter(({ sendAsEmail }) => sendAsEmail)
+ .map(({ sendAsEmail }) => sendAsEmail);
+ },
+ },
replyTo: {
type: "string",
label: "Reply To",
@@ -219,10 +231,16 @@ export default {
default: "plaintext",
options: Object.values(constants.BODY_TYPES),
},
- attachments: {
- type: "object",
- label: "Attachments",
- description: "Add any attachments you'd like to include as objects.\n- The `key` should be the filename and must contain the file extension (e.g. `.jpeg`, `.txt`).\n- The `value` should be a URL of the download link for the file, or the local path (e.g. `/tmp/my-file.txt`).",
+ attachmentFilenames: {
+ type: "string[]",
+ label: "Attachment Filenames",
+ description: "Array of the names of the files to attach. Must contain the file extension (e.g. `.jpeg`, `.txt`). Use in conjuction with `Attachment URLs or Paths`.",
+ optional: true,
+ },
+ attachmentUrlsOrPaths: {
+ type: "string[]",
+ label: "Attachment URLs or Paths",
+ description: "Array of the URLs of the download links for the files, or the local paths (e.g. `/tmp/my-file.txt`). Use in conjuction with `Attachment Filenames`.",
optional: true,
},
inReplyTo: {
@@ -260,11 +278,12 @@ export default {
name: fromName,
email,
} = await this.userInfo();
+ const fromEmail = props.fromEmail || email;
const opts = {
from: props.fromName
- ? `${props.fromName} <${email}>`
- : `${fromName} <${email}>`,
+ ? `${props.fromName} <${fromEmail}>`
+ : `${fromName} <${fromEmail}>`,
to: props.to,
cc: props.cc,
bcc: props.bcc,
@@ -289,18 +308,14 @@ export default {
}
}
- if (props.attachments) {
- if (typeof props.attachments === "string") {
- props.attachments = JSON.parse(props.attachments);
+ if (props.attachmentFilenames?.length && props.attachmentUrlsOrPaths?.length) {
+ opts.attachments = [];
+ for (let i = 0; i < props.attachmentFilenames.length; i++) {
+ opts.attachments.push({
+ filename: props.attachmentFilenames[i],
+ path: props.attachmentUrlsOrPaths[i],
+ });
}
- opts.attachments = Object.entries(props.attachments)
- .map(([
- filename,
- path,
- ]) => ({
- filename,
- path,
- }));
}
if (props.bodyType === constants.BODY_TYPES.HTML) {
diff --git a/components/gmail/package.json b/components/gmail/package.json
index 7bb5fea2b58db..4d03bb3779ae5 100644
--- a/components/gmail/package.json
+++ b/components/gmail/package.json
@@ -1,6 +1,6 @@
{
"name": "@pipedream/gmail",
- "version": "0.1.2",
+ "version": "0.1.3",
"description": "Pipedream Gmail Components",
"main": "gmail.app.mjs",
"keywords": [
diff --git a/components/gmail/sources/common/polling-history.mjs b/components/gmail/sources/common/polling-history.mjs
index b781e990a80e3..9fc8ae1f0a7b7 100644
--- a/components/gmail/sources/common/polling-history.mjs
+++ b/components/gmail/sources/common/polling-history.mjs
@@ -6,6 +6,9 @@ export default {
hooks: {
...common.hooks,
async deploy() {
+ if (this.triggerType === "webhook") {
+ return;
+ }
const historyId = await this.getHistoryId();
if (!historyId) {
return;
diff --git a/components/gmail/sources/common/verify-client-id.mjs b/components/gmail/sources/common/verify-client-id.mjs
index 708f42be33887..00563ded263d8 100644
--- a/components/gmail/sources/common/verify-client-id.mjs
+++ b/components/gmail/sources/common/verify-client-id.mjs
@@ -1,19 +1,6 @@
-import gmail from "../../gmail.app.mjs";
-
const restrictedClientId = "38931588176-fnd4m13k1mjb6djallp1m9kr7o8kslcu.apps.googleusercontent.com";
export default {
- props: {
- gmail: {
- ...gmail,
- reloadProps: true,
- },
- appAlert: {
- type: "alert",
- content: "",
- hidden: true,
- },
- },
methods: {
async checkClientId() {
return this.gmail.$auth.oauth_client_id !== restrictedClientId;
diff --git a/components/gmail/sources/new-attachment-received/new-attachment-received.mjs b/components/gmail/sources/new-attachment-received/new-attachment-received.mjs
index 5c38d8bac3ee7..ac831e1f5730e 100644
--- a/components/gmail/sources/new-attachment-received/new-attachment-received.mjs
+++ b/components/gmail/sources/new-attachment-received/new-attachment-received.mjs
@@ -6,7 +6,7 @@ export default {
key: "gmail-new-attachment-received",
name: "New Attachment Received",
description: "Emit new event for each attachment in a message received. This source is capped at 100 max new messages per run.",
- version: "0.0.2",
+ version: "0.0.3",
type: "source",
dedupe: "unique",
props: {
diff --git a/components/gmail/sources/new-email-matching-search/new-email-matching-search.mjs b/components/gmail/sources/new-email-matching-search/new-email-matching-search.mjs
index 7b3f44bcd49d4..aa5de10dac48d 100644
--- a/components/gmail/sources/new-email-matching-search/new-email-matching-search.mjs
+++ b/components/gmail/sources/new-email-matching-search/new-email-matching-search.mjs
@@ -6,7 +6,7 @@ export default {
key: "gmail-new-email-matching-search",
name: "New Email Matching Search",
description: "Emit new event when an email matching the search criteria is received. This source is capped at 100 max new messages per run.",
- version: "0.0.1",
+ version: "0.0.2",
type: "source",
dedupe: "unique",
props: {
diff --git a/components/gmail/sources/new-email-received-instant/new-email-received-instant.mjs b/components/gmail/sources/new-email-received-instant/new-email-received-instant.mjs
deleted file mode 100644
index 10b5d089bd29a..0000000000000
--- a/components/gmail/sources/new-email-received-instant/new-email-received-instant.mjs
+++ /dev/null
@@ -1,400 +0,0 @@
-import { axios } from "@pipedream/platform";
-import { PubSub } from "@google-cloud/pubsub";
-import { v4 as uuidv4 } from "uuid";
-import common from "../common/verify-client-id.mjs";
-
-export default {
- key: "gmail-new-email-received-instant",
- name: "New Email Received (Instant)",
- description: "Emit new event when a new email is received.",
- version: "0.0.1",
- type: "source",
- props: {
- instructionsAlert: {
- type: "alert",
- alertType: "info",
- content: "The Gmail - New Email Received (Instant) trigger requires users to configure a Custom OAuth client for Gmail, which is available on Pipedream's **Advanced** plan or higher. Please see the setup instructions [here](https://pipedream.com/apps/gmail/triggers/new-email-received-instant).",
- },
- ...common.props,
- http: "$.interface.http",
- timer: {
- type: "$.interface.timer",
- default: {
- intervalSeconds: 24 * 60 * 60,
- },
- hidden: true,
- },
- db: "$.service.db",
- topicType: {
- type: "string",
- label: "Pub/Sub Topic",
- description: "Do you have an existing Pub/Sub topic, or would you like to create a new one?",
- options: [
- "existing",
- "new",
- ],
- reloadProps: true,
- },
- label: {
- propDefinition: [
- common.props.gmail,
- "label",
- ],
- default: "INBOX",
- optional: true,
- },
- keyAlert: {
- type: "alert",
- alertType: "error",
- content: `No Service Account Key JSON found. Please reconnect your Gmail account in Pipedream, and add a Service Account Key JSON.
- \nIn order to receive real-time push notifications for changes to your email inbox, you will need to first enable the Google Cloud Pub/Sub API within your Google Cloud console.
- \n
- \n1. Navigate to the [Cloud Pub/Sub API](https://console.cloud.google.com/apis/library/pubsub.googleapis.com) and click “Enable.”
- \n2. Under “API and Services”, click “Credentials”
- \n3. Click “Create Credentials”, then “Service Account”
- \n4. Name your service account, e.g. “Gmail PubSub Handler”, and optionally provide a description, e.g. “Used for configuring Pub/Sub API with Gmail on Pipedream.”
- \n5. Click on your newly created service account. Under the heading “Keys”, click “Add Key” then “Create Key”, Key Type: JSON. The private key will be saved to your computer; be sure to store this securely.
- `,
- hidden: true,
- },
- permissionAlert: {
- type: "alert",
- alertType: "error",
- content: `Unable to grant publish permission to Gmail API service account.
- \n1. Navigate to your [Google Cloud PubSub Topics List](https://console.cloud.google.com/cloudpubsub)
- \n2. Select "View Permissions" for the topic you intend to use for this source.
- \n3. Click "ADD PRINCIPAL"
- \n4. Select "Pub/Sub Publisher" for the Role.
- \n5. Enter \`serviceAccount:gmail-api-push@system.gserviceaccount.com\` as the principal.
- \n6. Click "Save"
- `,
- hidden: true,
- },
- latencyWarningAlert: {
- type: "alert",
- alertType: "warning",
- content: "Please allow up to 1 minute for deployment. We're setting up your real-time email notifications behind the scenes.",
- hidden: true,
- },
- },
- async additionalProps(props) {
- const isValidClientId = await this.checkClientId();
- if (!isValidClientId) {
- props.appAlert.alertType = "error";
- props.appAlert.content = "You must use a custom OAuth client to use this component. Please see [here](https://pipedream.com/docs/connected-accounts/oauth-clients) for more details.";
- props.appAlert.hidden = false;
- return {};
- }
-
- const { key_json: key } = this.gmail.$auth;
- if (!key) {
- props.keyAlert.hidden = false;
- return {};
- }
- let topicName = this.topic;
- const topicProp = {
- type: "string",
- label: "Pub/Sub Topic Name",
- description: "Select a Pub/Sub topic from your GCP account to watch",
- options: async () => {
- return this.getTopics();
- },
- reloadProps: true,
- };
- if (this.topicType === "new") {
- const authKeyJSON = JSON.parse(key);
- const { project_id: projectId } = authKeyJSON;
- topicName = `projects/${projectId}/topics/${this.convertNameToValidPubSubTopicName(uuidv4())}`;
- topicProp.default = topicName;
- topicProp.hidden = true;
- topicProp.reloadProps = false;
- }
-
- const newProps = {
- topic: topicProp,
- };
-
- if (this.topic || this.topicType === "new") {
- const topic = await this.getOrCreateTopic(topicName);
-
- // Retrieves the IAM policy for the topic
- let hasPublisherRole;
- try {
- const [
- policy,
- ] = await topic.iam.getPolicy();
- hasPublisherRole = policy.bindings.find(({
- members, role,
- }) => members.includes("serviceAccount:gmail-api-push@system.gserviceaccount.com") && role === "roles/pubsub.publisher");
- } catch {
- console.log("Could not retrieve iam policy");
- }
-
- if (!hasPublisherRole) {
- // Grant publish permission to Gmail API service account
- try {
- await topic.iam.setPolicy({
- bindings: [
- {
- role: "roles/pubsub.publisher",
- members: [
- "serviceAccount:gmail-api-push@system.gserviceaccount.com",
- ],
- },
- ],
- });
- console.log("Permissions granted to Gmail API service account.");
- } catch {
- props.permissionAlert.hidden = false;
- return newProps;
- }
- }
-
- props.latencyWarningAlert.hidden = false;
-
- const historyId = await this.setupGmailNotifications(topicName);
- newProps.initialHistoryId = {
- type: "string",
- default: historyId,
- hidden: true,
- };
- }
-
- return newProps;
- },
- hooks: {
- async deploy() {
- this._setLastProcessedHistoryId(this.initialHistoryId);
- },
- async activate() {
- const sdkParams = this.sdkParams();
- const pubSubClient = new PubSub(sdkParams);
-
- const currentTopic = {
- name: this.topic,
- };
-
- // Create subscription
- const pushEndpoint = this.http.endpoint;
- const subscriptionName = this.convertNameToValidPubSubTopicName(pushEndpoint);
- const subscriptionOptions = {
- pushConfig: {
- pushEndpoint,
- },
- };
- const [
- subscriptionResult,
- ] = await pubSubClient
- .topic(currentTopic.name)
- .createSubscription(subscriptionName, subscriptionOptions);
- this._setSubscriptionName(subscriptionResult.name);
- },
- async deactivate() {
- const sdkParams = this.sdkParams();
- const pubSubClient = new PubSub(sdkParams);
-
- const subscriptionName = this._getSubscriptionName();
- if (subscriptionName) {
- await pubSubClient.subscription(subscriptionName).delete();
- }
- },
- },
- methods: {
- ...common.methods,
- _getTopicName() {
- return this.db.get("topicName");
- },
- _setTopicName(topicName) {
- this.db.set("topicName", topicName);
- },
- _getSubscriptionName() {
- return this.db.get("subscriptionName");
- },
- _setSubscriptionName(subscriptionName) {
- this.db.set("subscriptionName", subscriptionName);
- },
- _getLastProcessedHistoryId() {
- return this.db.get("lastProcessedHistoryId");
- },
- _setLastProcessedHistoryId(lastProcessedHistoryId) {
- this.db.set("lastProcessedHistoryId", lastProcessedHistoryId);
- },
- sdkParams() {
- const authKeyJSON = JSON.parse(this.gmail.$auth.key_json);
- const {
- project_id: projectId,
- client_email,
- private_key,
- } = authKeyJSON;
- const sdkParams = {
- credentials: {
- client_email,
- private_key,
- },
- projectId,
- };
- return sdkParams;
- },
- async getTopics() {
- const sdkParams = this.sdkParams();
- const pubSubClient = new PubSub(sdkParams);
- const topics = (await pubSubClient.getTopics())[0];
- if (topics.length > 0) {
- return topics.map((topic) => topic.name);
- }
- return [];
- },
- convertNameToValidPubSubTopicName(name) {
- // For valid names, see https://cloud.google.com/pubsub/docs/admin#resource_names
- return name
- // Must not start with `goog`. We add a `pd-` at the beginning if that's the case.
- .replace(/(^goog.*)/g, "pd-$1")
- // Must start with a letter, otherwise we add `pd-` at the beginning.
- .replace(/^(?![a-zA-Z]+)/, "pd-")
- // Only certain characters are allowed, the rest will be replaced with a `-`.
- .replace(/[^a-zA-Z0-9_\-.~+%]+/g, "-");
- },
- makeRequest({
- $ = this,
- path,
- ...opts
- }) {
- return axios($, {
- url: `https://gmail.googleapis.com/gmail/v1${path}`,
- headers: {
- Authorization: `Bearer ${this.gmail.getToken()}`,
- },
- ...opts,
- });
- },
- async setupGmailNotifications(topicName) {
- // Set up Gmail push notifications using OAuth token
- const watchResponse = await this.makeRequest({
- method: "POST",
- path: "/users/me/watch",
- data: {
- topicName,
- labelIds: [
- "INBOX",
- ],
- },
- });
- console.log("Watch response:", watchResponse);
- return watchResponse.historyId;
- },
- async getOrCreateTopic(name) {
- const sdkParams = this.sdkParams();
- const pubSubClient = new PubSub(sdkParams);
- const topicName = name || this.topic;
- // Create or get Pub/Sub topic
- let topic;
- try {
- [
- topic,
- ] = await pubSubClient.createTopic(topicName);
- console.log(`Topic ${topicName} created.`);
- } catch (error) {
- if (error.code === 6) { // Already exists
- topic = pubSubClient.topic(topicName);
- } else {
- throw error;
- }
- }
- return topic;
- },
- processEmails(messageDetails) {
- // Process and structure the email data
- return messageDetails.map((msg) => {
- const headers = msg.payload.headers;
- return {
- id: msg.id,
- threadId: msg.threadId,
- subject: headers.find((h) => h.name.toLowerCase() === "subject")?.value,
- from: headers.find((h) => h.name.toLowerCase() === "from")?.value,
- to: headers.find((h) => h.name.toLowerCase() === "to")?.value,
- date: headers.find((h) => h.name.toLowerCase() === "date")?.value,
- snippet: msg.snippet,
- };
- });
- },
- },
- async run(event) {
- if (event.timestamp) {
- // event was triggered by timer
- const topicName = this._getTopicName();
- if (topicName) {
- // renew Gmail push notifications
- await this.setupGmailNotifications(topicName);
- return;
- } else {
- // first run, no need to renew push notifications
- this._setTopicName(this.topic);
- return;
- }
- }
-
- // Extract the Pub/Sub message data
- const pubsubMessage = event.body.message;
- const decodedData = JSON.parse(Buffer.from(pubsubMessage.data, "base64").toString());
-
- console.log("Decoded Pub/Sub data:", decodedData);
-
- const { historyId: receivedHistoryId } = decodedData;
-
- // Retrieve the last processed historyId
- const lastProcessedHistoryId = this._getLastProcessedHistoryId();
- console.log("Last processed historyId:", lastProcessedHistoryId);
-
- // Use the minimum of lastProcessedHistoryId and the received historyId
- const startHistoryId = Math.min(parseInt(lastProcessedHistoryId), parseInt(receivedHistoryId));
- console.log("Using startHistoryId:", startHistoryId);
-
- // Fetch the history
- const historyResponse = await this.gmail.listHistory({
- startHistoryId,
- historyTypes: [
- "messageAdded",
- ],
- labelId: this.label,
- });
-
- console.log("History response:", JSON.stringify(historyResponse, null, 2));
-
- // Process history to find new messages
- const newMessages = [];
- if (historyResponse.history) {
- for (const historyItem of historyResponse.history) {
- if (historyItem.messagesAdded) {
- newMessages.push(...historyItem.messagesAdded.map((msg) => msg.message));
- }
- }
- }
-
- console.log("New messages found:", newMessages.length);
-
- // Fetch full message details for new messages
- const newMessageIds = newMessages?.map(({ id }) => id) || [];
- const messageDetails = await this.gmail.getMessages(newMessageIds);
-
- console.log("Fetched message details count:", messageDetails.length);
-
- const processedEmails = this.processEmails(messageDetails);
-
- // Store the latest historyId in the db
- const latestHistoryId = historyResponse.historyId || receivedHistoryId;
- this._setLastProcessedHistoryId(latestHistoryId);
- console.log("Updated lastProcessedHistoryId:", latestHistoryId);
-
- if (processedEmails?.length) {
- this.$emit({
- newEmailsCount: processedEmails.length,
- emails: processedEmails,
- lastProcessedHistoryId: latestHistoryId,
- }, {
- id: latestHistoryId,
- summary: processedEmails[0].subject,
- ts: Date.now(),
- });
- }
- },
-};
diff --git a/components/gmail/sources/new-email-received-instant/README.md b/components/gmail/sources/new-email-received/README.md
similarity index 88%
rename from components/gmail/sources/new-email-received-instant/README.md
rename to components/gmail/sources/new-email-received/README.md
index 800cfafff0095..365df99825e7b 100644
--- a/components/gmail/sources/new-email-received-instant/README.md
+++ b/components/gmail/sources/new-email-received/README.md
@@ -1,16 +1,16 @@
-# Gmail - New Email Received (Instant) Trigger Setup Guide
-
## Overview
-The Gmail - New Email Received (Instant) trigger requires users to be on Pipedream's Advanced plan or higher. This guide will walk you through the process of setting up real-time push notifications for changes to your Gmail inbox.
+The Gmail - New Email Received (Instant) source enables you to trigger Pipedream workflows based on real-time changes to your Gmail inbox.
+
+## Getting Started
-## Prerequisites
+### Prerequisites
- A Google Cloud account
- A Pipedream account on the Advanced plan or higher
- Basic familiarity with Google Cloud Console
-## Quickstart
+### Quickstart
1. Create a custom Gmail client in Google Cloud Console
2. Enable Gmail API and Pub/Sub API
@@ -20,9 +20,9 @@ The Gmail - New Email Received (Instant) trigger requires users to be on Pipedre
For detailed instructions, follow the steps below.
-## Detailed Setup Instructions
+### Detailed Setup Instructions
-### 1. Create a Gmail app
+#### 1. Create a Gmail app
1. Sign in to the [Google Cloud Console](https://console.cloud.google.com/welcome)
2. Select an existing project, or create a new one
@@ -46,7 +46,7 @@ For detailed instructions, follow the steps below.
> **Note:** If you encounter issues with API enablement, ensure you have the necessary permissions in your Google Cloud project.
-### 2. Set up the OAuth consent screen
+#### 2. Set up the OAuth consent screen
1. Click **OAuth consent screen** on the left side
@@ -61,7 +61,7 @@ For detailed instructions, follow the steps below.

-### 3. Create OAuth Credentials in Google and Custom OAuth Client in Pipedream
+#### 3. Create OAuth Credentials in Google and Custom OAuth Client in Pipedream
1. Navigate to the **Credentials** section on the left side.
@@ -99,7 +99,7 @@ For detailed instructions, follow the steps below.
> **Important:** When creating the OAuth client ID, make sure to copy the Redirect URI from Pipedream exactly as shown to avoid authentication errors.
-### 4. Create service account
+#### 4. Create service account
1. Navigate to **[Credentials](https://console.cloud.google.com/apis/credentials?)** under APIs & Services, and click **Create Credentials** > **Service Account**.
@@ -113,13 +113,12 @@ For detailed instructions, follow the steps below.

-### 5. Connect to Custom Gmail Client, with Service Account Credentials
+#### 5. Connect your Gmail account in Pipedream
-1. From the Pipedream Accounts page, click **OAuth Clients**. Next to your newly created Gmail client, click the three-dot menu on the righthand side and click **Connect Account**.
-2. Click on the optional field, **Service Account Key JSON**, and copy and paste the text from the service account credentials that were downloaded.
-3. Once you go through the authorization flow and have connected successfully, you should now be able to use the Gmail - New Email Received (Instant) trigger!
+1. From the Pipedream Accounts page, click **OAuth Clients**. Next to your newly created Gmail client, click the three-dot menu on the righthand side and click **Connect Account**. Or you can also connect your account from the workflow builder, when configuring the Gmail trigger.
+2. While configuring the New Email Received trigger, you should be prompted to input your Service Account Key JSON.
-### 6. Publish your custom Gmail app (required for External app type only)
+#### 6. Publish your custom Gmail app (required for External app type only)
Google has a [7 day expiration window](https://developers.google.com/identity/protocols/oauth2#:~:text=A%20Google%20Cloud,Connect%20equivalents) on refresh tokens for **External** applications with a publishing status of "Testing", so you will need to **Publish** your application in order to maintain your account connection.
@@ -138,4 +137,4 @@ Google has a [7 day expiration window](https://developers.google.com/identity/pr
- **API Not Enabled**: Ensure both Gmail API and Pub/Sub API are enabled in your Google Cloud project.
- **Service Account Issues**: Verify that your service account has the "Pub/Sub Admin" role and that you've correctly pasted the JSON key into Pipedream.
-If you continue to experience issues, please contact Pipedream support for further assistance.
\ No newline at end of file
+If you continue to experience issues, please contact Pipedream support for further assistance.
diff --git a/components/gmail/sources/new-email-received/new-email-received.mjs b/components/gmail/sources/new-email-received/new-email-received.mjs
index a8d9b8203b6ad..33aec26e6f3b2 100644
--- a/components/gmail/sources/new-email-received/new-email-received.mjs
+++ b/components/gmail/sources/new-email-received/new-email-received.mjs
@@ -1,5 +1,11 @@
import gmail from "../../gmail.app.mjs";
import common from "../common/polling-history.mjs";
+import {
+ axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, ConfigurationError,
+} from "@pipedream/platform";
+import { PubSub } from "@google-cloud/pubsub";
+import { v4 as uuidv4 } from "uuid";
+import verifyClient from "../common/verify-client-id.mjs";
export default {
...common,
@@ -7,21 +13,355 @@ export default {
name: "New Email Received",
description: "Emit new event when a new email is received.",
type: "source",
- version: "0.0.2",
+ version: "0.1.0",
dedupe: "unique",
props: {
- ...common.props,
gmail,
+ db: "$.service.db",
+ triggerType: {
+ type: "string",
+ label: "Trigger Type",
+ options: [
+ "webhook",
+ "polling",
+ ],
+ description: "Configuring this trigger as a `webhook` requires a Custom OAuth client, which is available on Pipedream's [Advanced plan or higher](https://pipedream.com/pricing?plan=Advanced). Refer to the getting started guide [here](https://pipedream.com/apps/gmail/triggers/new-email-received#getting-started).",
+ reloadProps: true,
+ },
+ serviceAccountKeyJson: {
+ type: "string",
+ label: "Service Account Key JSON",
+ optional: true,
+ hidden: true,
+ reloadProps: true,
+ },
+ serviceAccountKeyJsonInstructions: {
+ type: "alert",
+ alertType: "info",
+ content: `1) [Create a service account in GCP](https://cloud.google.com/iam/docs/creating-managing-service-accounts) and set the following permission: **Pub/Sub Admin**
+ \n2) [Generate a service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys)
+ \n3) Download the key details in JSON format
+ \n4) Open the JSON in a text editor, and **copy and paste its contents here**.
+ `,
+ hidden: true,
+ },
+ topicType: {
+ type: "string",
+ label: "Pub/Sub Topic",
+ description: "Do you have an existing Pub/Sub topic, or would you like to create a new one?",
+ options: [
+ "existing",
+ "new",
+ ],
+ optional: true,
+ hidden: true,
+ reloadProps: true,
+ },
+ topic: {
+ type: "string",
+ label: "Pub/Sub Topic Name",
+ description: "Select a Pub/Sub topic from your GCP account to watch",
+ async options() {
+ return this.getTopics();
+ },
+ optional: true,
+ hidden: true,
+ reloadProps: true,
+ },
label: {
propDefinition: [
gmail,
"label",
],
+ default: "INBOX",
optional: true,
+ hidden: true,
+ },
+ permissionAlert: {
+ type: "alert",
+ alertType: "error",
+ content: `Unable to grant publish permission to Gmail API service account.
+ \n1. Navigate to your [Google Cloud PubSub Topics List](https://console.cloud.google.com/cloudpubsub)
+ \n2. Select "View Permissions" for the topic you intend to use for this source.
+ \n3. Click "ADD PRINCIPAL"
+ \n4. Select "Pub/Sub Publisher" for the Role.
+ \n5. Enter \`serviceAccount:gmail-api-push@system.gserviceaccount.com\` as the principal.
+ \n6. Click "Save"
+ `,
+ hidden: true,
+ },
+ latencyWarningAlert: {
+ type: "alert",
+ alertType: "warning",
+ content: "Please allow up to 1 minute for deployment. We're setting up your real-time email notifications behind the scenes.",
+ hidden: true,
+ },
+ },
+ async additionalProps(props) {
+ const newProps = {};
+ if (this.triggerType === "polling") {
+ newProps.timer = {
+ type: "$.interface.timer",
+ default: {
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
+ },
+ };
+ }
+ if (this.triggerType === "webhook") {
+ // verify that a Custom OAuth client is being used
+ const isValidClientId = await this.checkClientId();
+ if (!isValidClientId) {
+ throw new ConfigurationError("You must use a custom OAuth client to use this component. Please see [here](https://pipedream.com/docs/connected-accounts/oauth-clients) for more details.");
+ }
+
+ newProps.http = "$.interface.http";
+ newProps.timer = {
+ type: "$.interface.timer",
+ default: {
+ intervalSeconds: 24 * 60 * 60,
+ },
+ hidden: true,
+ };
+
+ props.serviceAccountKeyJson.hidden = false;
+ props.serviceAccountKeyJson.optional = false;
+ props.serviceAccountKeyJsonInstructions.hidden = false;
+
+ if (!this.serviceAccountKeyJson) {
+ return newProps;
+ }
+
+ props.topicType.hidden = false;
+ props.topicType.optional = false;
+
+ if (!this.topicType) {
+ return newProps;
+ }
+
+ // create topic prop
+ let topicName = this.topic;
+ if (this.topicType === "new") {
+ const authKeyJSON = JSON.parse(this.serviceAccountKeyJson);
+ const { project_id: projectId } = authKeyJSON;
+ topicName = `projects/${projectId}/topics/${this.convertNameToValidPubSubTopicName(uuidv4())}`;
+ props.topic.default = topicName;
+ props.topic.reloadProps = false;
+ } else {
+ props.topic.hidden = false;
+ props.topic.optional = false;
+ }
+
+ if (this.topic || this.topicType === "new") {
+ const topic = await this.getOrCreateTopic(topicName);
+
+ // Retrieves the IAM policy for the topic
+ let hasPublisherRole;
+ try {
+ const [
+ policy,
+ ] = await topic.iam.getPolicy();
+ hasPublisherRole = policy.bindings.find(({
+ members, role,
+ }) => members.includes("serviceAccount:gmail-api-push@system.gserviceaccount.com") && role === "roles/pubsub.publisher");
+ } catch {
+ console.log("Could not retrieve iam policy");
+ }
+
+ if (!hasPublisherRole) {
+ // Grant publish permission to Gmail API service account
+ try {
+ await topic.iam.setPolicy({
+ bindings: [
+ {
+ role: "roles/pubsub.publisher",
+ members: [
+ "serviceAccount:gmail-api-push@system.gserviceaccount.com",
+ ],
+ },
+ ],
+ });
+ console.log("Permissions granted to Gmail API service account.");
+ } catch {
+ props.permissionAlert.hidden = false;
+ return newProps;
+ }
+ }
+
+ props.latencyWarningAlert.hidden = false;
+
+ const historyId = await this.setupGmailNotifications(topicName);
+ newProps.initialHistoryId = {
+ type: "string",
+ default: historyId,
+ hidden: true,
+ };
+ }
+ }
+ props.label.hidden = false;
+ return newProps;
+ },
+ hooks: {
+ ...common.hooks,
+ async activate() {
+ if (this.triggerType === "polling") {
+ return;
+ }
+
+ const sdkParams = this.sdkParams();
+ const pubSubClient = new PubSub(sdkParams);
+
+ const currentTopic = {
+ name: this.topic,
+ };
+
+ // Create subscription
+ const pushEndpoint = this.http.endpoint;
+ const subscriptionName = this.convertNameToValidPubSubTopicName(pushEndpoint);
+ const subscriptionOptions = {
+ pushConfig: {
+ pushEndpoint,
+ },
+ };
+ const [
+ subscriptionResult,
+ ] = await pubSubClient
+ .topic(currentTopic.name)
+ .createSubscription(subscriptionName, subscriptionOptions);
+ this._setSubscriptionName(subscriptionResult.name);
+ },
+ async deactivate() {
+ if (this.triggerType === "polling") {
+ return;
+ }
+
+ const sdkParams = this.sdkParams();
+ const pubSubClient = new PubSub(sdkParams);
+
+ const subscriptionName = this._getSubscriptionName();
+ if (subscriptionName) {
+ await pubSubClient.subscription(subscriptionName).delete();
+ }
},
},
methods: {
+ ...verifyClient.methods,
...common.methods,
+ _getTopicName() {
+ return this.db.get("topicName");
+ },
+ _setTopicName(topicName) {
+ this.db.set("topicName", topicName);
+ },
+ _getSubscriptionName() {
+ return this.db.get("subscriptionName");
+ },
+ _setSubscriptionName(subscriptionName) {
+ this.db.set("subscriptionName", subscriptionName);
+ },
+ _getLastProcessedHistoryId() {
+ return this.db.get("lastProcessedHistoryId");
+ },
+ _setLastProcessedHistoryId(lastProcessedHistoryId) {
+ this.db.set("lastProcessedHistoryId", lastProcessedHistoryId);
+ },
+ sdkParams() {
+ const authKeyJSON = JSON.parse(this.serviceAccountKeyJson);
+ const {
+ project_id: projectId,
+ client_email,
+ private_key,
+ } = authKeyJSON;
+ const sdkParams = {
+ credentials: {
+ client_email,
+ private_key,
+ },
+ projectId,
+ };
+ return sdkParams;
+ },
+ async getTopics() {
+ const sdkParams = this.sdkParams();
+ const pubSubClient = new PubSub(sdkParams);
+ const topics = (await pubSubClient.getTopics())[0];
+ if (topics.length > 0) {
+ return topics.map((topic) => topic.name);
+ }
+ return [];
+ },
+ convertNameToValidPubSubTopicName(name) {
+ // For valid names, see https://cloud.google.com/pubsub/docs/admin#resource_names
+ return name
+ // Must not start with `goog`. We add a `pd-` at the beginning if that's the case.
+ .replace(/(^goog.*)/g, "pd-$1")
+ // Must start with a letter, otherwise we add `pd-` at the beginning.
+ .replace(/^(?![a-zA-Z]+)/, "pd-")
+ // Only certain characters are allowed, the rest will be replaced with a `-`.
+ .replace(/[^a-zA-Z0-9_\-.~+%]+/g, "-");
+ },
+ makeRequest({
+ $ = this,
+ path,
+ ...opts
+ }) {
+ return axios($, {
+ url: `https://gmail.googleapis.com/gmail/v1${path}`,
+ headers: {
+ Authorization: `Bearer ${this.gmail.getToken()}`,
+ },
+ ...opts,
+ });
+ },
+ async setupGmailNotifications(topicName) {
+ // Set up Gmail push notifications using OAuth token
+ const watchResponse = await this.makeRequest({
+ method: "POST",
+ path: "/users/me/watch",
+ data: {
+ topicName,
+ labelIds: [
+ this.label || "INBOX",
+ ],
+ },
+ });
+ console.log("Watch response:", watchResponse);
+ return watchResponse.historyId;
+ },
+ async getOrCreateTopic(name) {
+ const sdkParams = this.sdkParams();
+ const pubSubClient = new PubSub(sdkParams);
+ const topicName = name || this.topic;
+ // Create or get Pub/Sub topic
+ let topic;
+ try {
+ [
+ topic,
+ ] = await pubSubClient.createTopic(topicName);
+ console.log(`Topic ${topicName} created.`);
+ } catch (error) {
+ if (error.code === 6) { // Already exists
+ topic = pubSubClient.topic(topicName);
+ } else {
+ throw error;
+ }
+ }
+ return topic;
+ },
+ processEmails(messageDetails) {
+ // Process and structure the email data
+ return messageDetails.map((msg) => {
+ const headers = msg.payload.headers;
+ return {
+ id: msg.id,
+ threadId: msg.threadId,
+ subject: headers.find((h) => h.name.toLowerCase() === "subject")?.value,
+ from: headers.find((h) => h.name.toLowerCase() === "from")?.value,
+ to: headers.find((h) => h.name.toLowerCase() === "to")?.value,
+ date: headers.find((h) => h.name.toLowerCase() === "date")?.value,
+ snippet: msg.snippet,
+ };
+ });
+ },
getHistoryTypes() {
return [
"messageAdded",
@@ -43,4 +383,99 @@ export default {
: history.filter((item) => item.messagesAdded?.length);
},
},
+ async run(event) {
+ if (this.triggerType === "polling") {
+ let lastHistoryId = this._getLastHistoryId();
+
+ if (!lastHistoryId) {
+ lastHistoryId = await this.getHistoryId();
+ }
+ await this.emitHistories(lastHistoryId);
+ }
+
+ if (this.triggerType === "webhook") {
+ if (event.timestamp) {
+ // event was triggered by timer
+ const topicName = this._getTopicName();
+ if (topicName) {
+ // renew Gmail push notifications
+ await this.setupGmailNotifications(topicName);
+ return;
+ } else {
+ // first run, no need to renew push notifications
+ this._setTopicName(this.topic);
+ this._setLastProcessedHistoryId(this.initialHistoryId);
+ return;
+ }
+ }
+
+ // Extract the Pub/Sub message data
+ const pubsubMessage = event.body.message;
+ if (!pubsubMessage) {
+ return;
+ }
+ const decodedData = JSON.parse(Buffer.from(pubsubMessage.data, "base64").toString());
+
+ console.log("Decoded Pub/Sub data:", decodedData);
+
+ const { historyId: receivedHistoryId } = decodedData;
+
+ // Retrieve the last processed historyId
+ const lastProcessedHistoryId = this._getLastProcessedHistoryId();
+ console.log("Last processed historyId:", lastProcessedHistoryId);
+
+ // Use the minimum of lastProcessedHistoryId and the received historyId
+ const startHistoryId =
+ Math.min(parseInt(lastProcessedHistoryId), parseInt(receivedHistoryId));
+ console.log("Using startHistoryId:", startHistoryId);
+
+ // Fetch the history
+ const historyResponse = await this.gmail.listHistory({
+ startHistoryId,
+ historyTypes: [
+ "messageAdded",
+ ],
+ labelId: this.label,
+ });
+
+ console.log("History response:", JSON.stringify(historyResponse, null, 2));
+
+ // Process history to find new messages
+ const newMessages = [];
+ if (historyResponse.history) {
+ for (const historyItem of historyResponse.history) {
+ if (historyItem.messagesAdded) {
+ newMessages.push(...historyItem.messagesAdded.map((msg) => msg.message));
+ }
+ }
+ }
+
+ console.log("New messages found:", newMessages.length);
+
+ // Fetch full message details for new messages
+ const newMessageIds = newMessages?.map(({ id }) => id) || [];
+ const messageDetails = await this.gmail.getMessages(newMessageIds);
+
+ console.log("Fetched message details count:", messageDetails.length);
+
+ const processedEmails = this.processEmails(messageDetails);
+
+ // Store the latest historyId in the db
+ const latestHistoryId = historyResponse.historyId || receivedHistoryId;
+ this._setLastProcessedHistoryId(latestHistoryId);
+ console.log("Updated lastProcessedHistoryId:", latestHistoryId);
+
+ if (processedEmails?.length) {
+ this.$emit({
+ newEmailsCount: processedEmails.length,
+ emails: processedEmails,
+ lastProcessedHistoryId: latestHistoryId,
+ }, {
+ id: processedEmails[0].id,
+ summary: processedEmails[0].subject,
+ ts: Date.now(),
+ });
+ }
+ }
+ },
};
diff --git a/components/gmail/sources/new-labeled-email/new-labeled-email.mjs b/components/gmail/sources/new-labeled-email/new-labeled-email.mjs
index b728b034e4f8a..0a186606d37da 100644
--- a/components/gmail/sources/new-labeled-email/new-labeled-email.mjs
+++ b/components/gmail/sources/new-labeled-email/new-labeled-email.mjs
@@ -8,7 +8,7 @@ export default {
name: "New Labeled Email",
description: "Emit new event when a new email is labeled.",
type: "source",
- version: "0.0.2",
+ version: "0.0.3",
dedupe: "unique",
props: {
...common.props,
diff --git a/components/gmail/sources/new-sent-email/new-sent-email.mjs b/components/gmail/sources/new-sent-email/new-sent-email.mjs
index a4dc75103ceed..8c2d9712c7173 100644
--- a/components/gmail/sources/new-sent-email/new-sent-email.mjs
+++ b/components/gmail/sources/new-sent-email/new-sent-email.mjs
@@ -6,7 +6,7 @@ export default {
key: "gmail-new-sent-email",
name: "New Sent Email",
description: "Emit new event for each new email sent. (Maximum of 100 events emited per execution)",
- version: "0.0.2",
+ version: "0.0.3",
type: "source",
dedupe: "unique",
props: {
diff --git a/components/gmail_custom_oauth/README.md b/components/gmail_custom_oauth/README.md
deleted file mode 100644
index 948e49b2042e5..0000000000000
--- a/components/gmail_custom_oauth/README.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# Overview
-With the Gmail (Developer App) API on Pipedream, you can automate a variety of email-related tasks, directly manipulating your Gmail account to streamline workflows. From sending emails programmatically to parsing new inbound messages for data extraction, the API allows for deep interaction with your Gmail inbox. Use cases include automating follow-ups, organizing your inbox with filters and labels, and synchronizing important email events with other services to act upon new information instantly.
-
-
-# Getting Started
-
-The Google Developer App in Pipedream can integrate with either a personal Gmail account or a Google workspace email account. Either option involves creating a custom Google App in the Google Cloud Console. This process does not involve any code or special approval by Google. The steps are outlined below:
-
-## Creating a Gmail app
-
-In order to connect your personal or workspace Gmail account to Pipedream, you'll need to create a custom OAuth app in Google Cloud.
-
-1. Sign in to the [Google Cloud Console](https://cloud.google.com/)
-2. Select an existing project or create a new one
-
- 
-
-3. Select **APIs & Services**
-4. Click **Enable APIs & Services**
-
- 
-
-5. Search for and select **Gmail API**
-6. Click **Enable**
-
- 
-
-7. Click **OAuth consent screen** on the left side
-
- 
-
-8. Select **External** User Type and click “Create”
-
- 
-
-9. Fill in the required fields and click **Save and Continue**
-10. Click **Add or remove scopes** and select the `https://mail.google.com/` scope and then click "Update"
-11. Click **Save and Continue** to finish the **Scopes** step
-12. Add your own email as a **Test User** by clicking **Add Users*& then typing in your email in the prompt then clicking **Add** again. Then finally click **Save and Continue** to finish the Test Users portion.
-13. You should be prompted with a **Summary** page.
-
-Now you've created an unlisted Gmail App that you can integrate with Pipedream.
-
-## Create OAuth Credentials
-
-You will need to generate a set of OAuth credentials to connect your new Gmail app to Pipedream properly.
-
-1. Navigate to the **Credentials** section on the left side.
-
- 
-
-2. Click **Create Credentials** at the top and select **“*OAuth client ID**
-
- 
-
- 
-
-3. Select **Web application** for **Application type**
-
- 
-
-4. Name the app “Pipedream”
-5. Click **Add URI** and enter `https://api.pipedream.com/connect/oauth/oa_G7Ain6/callback`
-
- 
-
-6. Click **Create** to create your new OAuth keys
-7. Note the client ID and client Secret, but keep these private and secure
-
- 
-
-## Connect your Gmail app Pipedream with your Gmail app OAuth crendentials
-
-At this point, you should have a Gmail App under your Google Project, and a set of OAuth keys.
-
-1. Now when prompted in Pipedream after trying to connect a Gmail Developer App, copy and paste your OAuth credentials.
-2. Also select the scopes you chose when defining the app. We recommend using `https://mail.google.com/`
-3. Then click **Connect**
-4. If you did not publish your Gmail App in the Google Cloud Console, just click **Continue** to ignore the warning.
-
- 
-
-5. Check all of the necessary scopes you'll need for your workflows
-
- 
-
-7. Click the final **Connect** and your custom Gmail app should be integrated into Pipedream!
-
-## Publish your custom Gmail app
-Google has a [7 day expiration window](https://developers.google.com/identity/protocols/oauth2#:~:text=A%20Google%20Cloud,Connect%20equivalents) on refresh tokens for applications with a publishing status of "Testing", so you will need to **Publish** your application in order to maintain your account connection.
-
-1. Navigate to your application, and click **OAuth Consent Screen** on the lefthand sidebar.
-2. Under **Publishing status**, click **Publish App**. If you included any sensitive or restricted scopes in your app, there will be a disclosure stating that you will need to go through the process of verification. Click **Confirm**.
-3. Your application will not be available externally unless you share your **client_id** with others, and you will not have to go through the verification process unless you intend to onboard over 100 users.
-4. The publishing status should be set to **In production**, and your account should maintain its connection without an expiration window.
-
-
-
-
-
-# Example Use Cases
-
-- **Email Digest Aggregator**: Collect and send a daily digest email of all the new messages from a specific label or search query. This could integrate with Slack, sending a summary message to a designated channel every morning.
-
-- **Customer Support Auto-Responder**: Instantly acknowledge incoming support requests by triggering an auto-response email. This workflow can be enhanced by using sentiment analysis via an AI service to categorize the urgency of the request.
-
-- **Invoice Processing**: Detect emails with invoices attached, extract relevant data, and input it into an accounting app like QuickBooks. Upon receipt, the workflow can also send back a confirmation email to the sender.
-
-# Troubleshooting
-**Application disconnects after 7 days**
-If your developer application disconnects after 7 days, you need to follow the steps above to Publish your custom Gmail app in order to keep your account connected.
\ No newline at end of file
diff --git a/components/gmail_custom_oauth/actions/add-label-to-email/add-label-to-email.mjs b/components/gmail_custom_oauth/actions/add-label-to-email/add-label-to-email.mjs
deleted file mode 100644
index fb0b0813645a7..0000000000000
--- a/components/gmail_custom_oauth/actions/add-label-to-email/add-label-to-email.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/add-label-to-email/add-label-to-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-add-label-to-email",
- version: "0.0.13",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/create-draft/create-draft.mjs b/components/gmail_custom_oauth/actions/create-draft/create-draft.mjs
deleted file mode 100644
index 1ce64be299171..0000000000000
--- a/components/gmail_custom_oauth/actions/create-draft/create-draft.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/create-draft/create-draft.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-create-draft",
- version: "0.0.9",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/download-attachment/download-attachment.mjs b/components/gmail_custom_oauth/actions/download-attachment/download-attachment.mjs
deleted file mode 100644
index 6c8c20dd1f5e5..0000000000000
--- a/components/gmail_custom_oauth/actions/download-attachment/download-attachment.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/download-attachment/download-attachment.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-download-attachment",
- version: "0.0.8",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/find-email/find-email.mjs b/components/gmail_custom_oauth/actions/find-email/find-email.mjs
deleted file mode 100644
index f1ea107b58c3d..0000000000000
--- a/components/gmail_custom_oauth/actions/find-email/find-email.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/find-email/find-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-find-email",
- version: "0.0.14",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/remove-label-from-email/remove-label-from-email.mjs b/components/gmail_custom_oauth/actions/remove-label-from-email/remove-label-from-email.mjs
deleted file mode 100644
index 9041f33273c70..0000000000000
--- a/components/gmail_custom_oauth/actions/remove-label-from-email/remove-label-from-email.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/remove-label-from-email/remove-label-from-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-remove-label-from-email",
- version: "0.0.1",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/send-email/send-email.mjs b/components/gmail_custom_oauth/actions/send-email/send-email.mjs
deleted file mode 100644
index c7258e6d7ab6b..0000000000000
--- a/components/gmail_custom_oauth/actions/send-email/send-email.mjs
+++ /dev/null
@@ -1,26 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-version */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/send-email/send-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-send-email",
- version: "0.1.6",
- props: {
- ...base.props,
- inReplyTo: {
- propDefinition: [
- base.props.gmail,
- "message",
- ],
- label: "In Reply To",
- description: "Specify the `message-id` this email is replying to.",
- optional: true,
- },
- },
-};
diff --git a/components/gmail_custom_oauth/actions/update-org-signature/update-org-signature.mjs b/components/gmail_custom_oauth/actions/update-org-signature/update-org-signature.mjs
deleted file mode 100644
index 53ce75db2e6ee..0000000000000
--- a/components/gmail_custom_oauth/actions/update-org-signature/update-org-signature.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/update-org-signature/update-org-signature.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-update-org-signature",
- version: "0.0.10",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/actions/update-primary-signature/update-primary-signature.mjs b/components/gmail_custom_oauth/actions/update-primary-signature/update-primary-signature.mjs
deleted file mode 100644
index a1ac62f1f3412..0000000000000
--- a/components/gmail_custom_oauth/actions/update-primary-signature/update-primary-signature.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-disable pipedream/required-properties-name */
-/* eslint-disable pipedream/required-properties-description */
-/* eslint-disable pipedream/required-properties-type */
-import base from "../../../gmail/actions/update-primary-signature/update-primary-signature.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-update-primary-signature",
- version: "0.0.10",
- type: "action",
-};
diff --git a/components/gmail_custom_oauth/common/override-app.mjs b/components/gmail_custom_oauth/common/override-app.mjs
deleted file mode 100644
index 448f6e0626ec3..0000000000000
--- a/components/gmail_custom_oauth/common/override-app.mjs
+++ /dev/null
@@ -1,23 +0,0 @@
-import app from "../gmail_custom_oauth.app.mjs";
-const appName = "gmail";
-
-/**
- * This function substitutes the app so that $auth and propDefinitions correspond to the correct app
- * This is done to avoid duplicating code because all component props and methods are shared between
- * gmail_custom_oauth and gmail apps
- * @param {*} base app to inject - in this case, gmail_custom_oauth
- */
-function overrideApp(base) {
- base.props[appName] = app;
-
- Object.keys(base.props)
- .filter((prop) => prop != appName)
- .forEach((prop) => {
- const { propDefinition } = base.props[prop];
- if (propDefinition?.length > 0) {
- propDefinition[0] = app;
- }
- });
-}
-
-export default overrideApp;
diff --git a/components/gmail_custom_oauth/gmail_custom_oauth.app.mjs b/components/gmail_custom_oauth/gmail_custom_oauth.app.mjs
deleted file mode 100644
index d56597b3f036a..0000000000000
--- a/components/gmail_custom_oauth/gmail_custom_oauth.app.mjs
+++ /dev/null
@@ -1,33 +0,0 @@
-import { axios } from "@pipedream/platform";
-import base from "../gmail/gmail.app.mjs";
-
-export default {
- ...base,
- type: "app",
- app: "gmail_custom_oauth",
- methods: {
- ...base.methods,
- getToken() {
- return this.$auth.oauth_access_token;
- },
- _apiUrl() {
- return "https://www.googleapis.com/gmail/v1/users/me";
- },
- _getHeaders() {
- return {
- "Authorization": `Bearer ${this.getToken()}`,
- };
- },
- async _makeRequest({
- $ = this, path, ...opts
- }) {
- const config = {
- url: `${this._apiUrl()}/${path}`,
- headers: this._getHeaders(),
- ...opts,
- };
-
- return axios($, config);
- },
- },
-};
diff --git a/components/gmail_custom_oauth/package.json b/components/gmail_custom_oauth/package.json
deleted file mode 100644
index 7d0bba0975a29..0000000000000
--- a/components/gmail_custom_oauth/package.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "name": "@pipedream/gmail_custom_oauth",
- "version": "0.1.13",
- "description": "Pipedream Gmail (Consumer) Components",
- "main": "gmail_custom_oauth.app.mjs",
- "keywords": [
- "pipedream",
- "gmail_custom_oauth"
- ],
- "homepage": "https://pipedream.com/apps/gmail_custom_oauth",
- "author": "Pipedream (https://pipedream.com/)",
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@google-cloud/pubsub": "^4.7.0",
- "@googleapis/gmail": "^0.3.4",
- "@pipedream/platform": "^3.0.0",
- "googleapis": "^109.0.1",
- "html-to-text": "^8.2.1",
- "nodemailer": "^6.7.8",
- "uuid": "^10.0.0"
- }
-}
diff --git a/components/gmail_custom_oauth/sources/new-attachment-received/new-attachment-received.mjs b/components/gmail_custom_oauth/sources/new-attachment-received/new-attachment-received.mjs
deleted file mode 100644
index 68de958edb56e..0000000000000
--- a/components/gmail_custom_oauth/sources/new-attachment-received/new-attachment-received.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import base from "../../../gmail/sources/new-attachment-received/new-attachment-received.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-attachment-received",
- name: "New Attachment Received",
- description: "Emit new event for each attachment in a message received. This source is capped at 100 max new messages per run.",
- version: "0.0.12",
- type: "source",
- dedupe: "unique",
-};
diff --git a/components/gmail_custom_oauth/sources/new-email-matching-search/new-email-matching-search.mjs b/components/gmail_custom_oauth/sources/new-email-matching-search/new-email-matching-search.mjs
deleted file mode 100644
index c5f6cb99844cc..0000000000000
--- a/components/gmail_custom_oauth/sources/new-email-matching-search/new-email-matching-search.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import base from "../../../gmail/sources/new-email-matching-search/new-email-matching-search.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-email-matching-search",
- name: "New Email Matching Search",
- description: "Emit new event when an email matching the search criteria is received. This source is capped at 100 max new messages per run.",
- version: "0.0.1",
- type: "source",
- dedupe: "unique",
-};
diff --git a/components/gmail_custom_oauth/sources/new-email-received-instant/new-email-received-instant.mjs b/components/gmail_custom_oauth/sources/new-email-received-instant/new-email-received-instant.mjs
deleted file mode 100644
index d9f62a25ccea3..0000000000000
--- a/components/gmail_custom_oauth/sources/new-email-received-instant/new-email-received-instant.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import base from "../../../gmail/sources/new-email-received-instant/new-email-received-instant.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-email-received-instant",
- name: "New Email Received (Instant)",
- description: "Emit new event when a new email is received.",
- type: "source",
- version: "0.0.1",
- dedupe: "unique",
-};
diff --git a/components/gmail_custom_oauth/sources/new-email-received/new-email-received.mjs b/components/gmail_custom_oauth/sources/new-email-received/new-email-received.mjs
deleted file mode 100644
index b5fda0807c7e5..0000000000000
--- a/components/gmail_custom_oauth/sources/new-email-received/new-email-received.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import base from "../../../gmail/sources/new-email-received/new-email-received.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-email-received",
- name: "New Email Received",
- description: "Emit new event when a new email is received.",
- type: "source",
- version: "0.0.13",
- dedupe: "unique",
-};
diff --git a/components/gmail_custom_oauth/sources/new-labeled-email/new-labeled-email.mjs b/components/gmail_custom_oauth/sources/new-labeled-email/new-labeled-email.mjs
deleted file mode 100644
index e452960d8521b..0000000000000
--- a/components/gmail_custom_oauth/sources/new-labeled-email/new-labeled-email.mjs
+++ /dev/null
@@ -1,16 +0,0 @@
-import sampleEmit from "./test-event.mjs";
-import base from "../../../gmail/sources/new-labeled-email/new-labeled-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-labeled-email",
- name: "New Labeled Email",
- description: "Emit new event when a new email is labeled.",
- type: "source",
- version: "0.0.10",
- dedupe: "unique",
- sampleEmit,
-};
diff --git a/components/gmail_custom_oauth/sources/new-labeled-email/test-event.mjs b/components/gmail_custom_oauth/sources/new-labeled-email/test-event.mjs
deleted file mode 100644
index ef2a280a89f79..0000000000000
--- a/components/gmail_custom_oauth/sources/new-labeled-email/test-event.mjs
+++ /dev/null
@@ -1,62 +0,0 @@
-export default {
- "id": "1669d3bc6ac196e4",
- "threadId": "1669d3bc6ac196e4",
- "labelIds": [
- "Label_17",
- "CATEGORY_PERSONAL"
- ],
- "snippet": "Hello",
- "payload": {
- "partId": "",
- "mimeType": "multipart/alternative",
- "filename": "",
- "headers": [],
- "body": {
- "size": 0
- },
- "parts": [
- {
- "partId": "0",
- "mimeType": "text/plain",
- "filename": "",
- "headers": [
- {
- "name": "Content-Type",
- "value": "text/plain; charset=\"UTF-8\""
- },
- {
- "name": "Content-Transfer-Encoding",
- "value": "quoted-printable"
- }
- ],
- "body": {
- "size": 69,
- "data": "SGV5L8NCuGQpw0K"
- }
- },
- {
- "partId": "1",
- "mimeType": "text/html",
- "filename": "",
- "headers": [
- {
- "name": "Content-Type",
- "value": "text/html; charset=\"UTF-8\""
- },
- {
- "name": "Content-Transfer-Encoding",
- "value": "quoted-printable"
- }
- ],
- "body": {
- "size": 390,
- "data": "PGRpd9udCB0PjwvZGl2Pg0K"
- }
- }
- ]
- },
- "sizeEstimate": 5224,
- "historyId": "2423873",
- "internalDate": "1540236205000",
- "decodedContent": "Hello\r\nᐧ\r\n"
-}
\ No newline at end of file
diff --git a/components/gmail_custom_oauth/sources/new-sent-email/new-sent-email.mjs b/components/gmail_custom_oauth/sources/new-sent-email/new-sent-email.mjs
deleted file mode 100644
index 23a68818f9297..0000000000000
--- a/components/gmail_custom_oauth/sources/new-sent-email/new-sent-email.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import base from "../../../gmail/sources/new-sent-email/new-sent-email.mjs";
-import overrideApp from "../../common/override-app.mjs";
-
-overrideApp(base);
-
-export default {
- ...base,
- key: "gmail_custom_oauth-new-sent-email",
- name: "New Sent Email",
- description: "Emit new event for each new email sent. (Maximum of 100 events emitted per execution)",
- version: "0.0.9",
- type: "source",
- dedupe: "unique",
-};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bc816ddbe582b..dc346f8657150 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3782,24 +3782,6 @@ importers:
nodemailer: 6.9.5
uuid: 10.0.0
- components/gmail_custom_oauth:
- specifiers:
- '@google-cloud/pubsub': ^4.7.0
- '@googleapis/gmail': ^0.3.4
- '@pipedream/platform': ^3.0.0
- googleapis: ^109.0.1
- html-to-text: ^8.2.1
- nodemailer: ^6.7.8
- uuid: ^10.0.0
- dependencies:
- '@google-cloud/pubsub': 4.7.0
- '@googleapis/gmail': 0.3.4
- '@pipedream/platform': 3.0.1
- googleapis: 109.0.1
- html-to-text: 8.2.1
- nodemailer: 6.9.5
- uuid: 10.0.0
-
components/gmodstore:
specifiers: {}
@@ -26935,17 +26917,6 @@ packages:
- supports-color
dev: false
- /googleapis/109.0.1:
- resolution: {integrity: sha512-x286OtNu0ngzxfGz2XgRs4aMhrwutRCkCE12dh2M1jIZOpOndB7ELFXEhmtxaJ7z3257flKIbiiCJZeBO+ze/Q==}
- engines: {node: '>=12.0.0'}
- dependencies:
- google-auth-library: 8.9.0
- googleapis-common: 6.0.4
- transitivePeerDependencies:
- - encoding
- - supports-color
- dev: false
-
/googleapis/131.0.0:
resolution: {integrity: sha512-fa4kdkY0VwHDw/04ItpQv2tlvlPIwbh6NjHDoWAVrV52GuaZbYCMOC5Y+hRmprp5HHIMRODmyb2YujlbZSRUbQ==}
engines: {node: '>=14.0.0'}