@pipedream/brevo 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/add-or-update-contact/add-or-update-contact.mjs +100 -0
- package/actions/send-transactional-email/send-transactional-email.mjs +171 -0
- package/brevo.app.mjs +247 -0
- package/common/options.mjs +26 -0
- package/package.json +5 -5
- package/sources/common-webhook.mjs +52 -0
- package/sources/marketing-webhook/marketing-webhook.mjs +46 -0
- package/sources/marketing-webhook/test-event.mjs +18 -0
- package/sources/transactional-webhook/test-event.mjs +18 -0
- package/sources/transactional-webhook/transactional-webhook.mjs +46 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import brevo from "../../brevo.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "brevo-add-or-update-contact",
|
|
5
|
+
name: "Add or Update a contact",
|
|
6
|
+
description: "Add or Update a contact",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
props: {
|
|
10
|
+
brevo,
|
|
11
|
+
providedIdentifier: {
|
|
12
|
+
type: "string",
|
|
13
|
+
label: "Contact",
|
|
14
|
+
description: "Email OR ID of the contact, if not present the contact will be inserted, it will be used to search the contact, if it can be found the contact will be updated else it will be inserted",
|
|
15
|
+
optional: true,
|
|
16
|
+
reloadProps: true,
|
|
17
|
+
async options({ prevContext }) {
|
|
18
|
+
return this.brevo.getContactsPaginated(prevContext, false);
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
email: {
|
|
22
|
+
type: "string",
|
|
23
|
+
label: "Email",
|
|
24
|
+
description: "To either be inserted or updated",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
async additionalProps() {
|
|
28
|
+
const attributesList = await this.brevo.getContactAttributes(this);
|
|
29
|
+
const attributesFields = attributesList.attributes
|
|
30
|
+
.filter((attr) => attr.category === "normal")
|
|
31
|
+
.reduce((acc, actual) => {
|
|
32
|
+
acc[`attribute-${actual.name}`] = {
|
|
33
|
+
type: "string",
|
|
34
|
+
label: actual.name.replace(/(^\w|\s\w)(\S*)/g, (_, m1, m2) => m1.toUpperCase() + m2.toLowerCase()),
|
|
35
|
+
description: "To either be inserted or updated",
|
|
36
|
+
optional: true,
|
|
37
|
+
};
|
|
38
|
+
return acc;
|
|
39
|
+
}, {});
|
|
40
|
+
const dynamicProps = {
|
|
41
|
+
...attributesFields,
|
|
42
|
+
listIds: {
|
|
43
|
+
type: "string[]",
|
|
44
|
+
label: "Lists",
|
|
45
|
+
description: "Array with ids of each list to be either inserted or updated,\n\n**On update the contact will be removed from previous lists**",
|
|
46
|
+
options: async ({ prevContext }) => {
|
|
47
|
+
return this.brevo.getListsPaginated(prevContext);
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return dynamicProps;
|
|
52
|
+
},
|
|
53
|
+
async run({ $ }) {
|
|
54
|
+
let identifier = this.providedIdentifier;
|
|
55
|
+
const listIds = Object.keys(this.listIds).map((key) => parseInt(this.listIds[key], 10));
|
|
56
|
+
let contact = null;
|
|
57
|
+
if (identifier) {
|
|
58
|
+
contact = await this.brevo.existingContactByIdentifier(
|
|
59
|
+
$,
|
|
60
|
+
encodeURIComponent(identifier),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const attributes = {
|
|
65
|
+
EMAIL: this.email,
|
|
66
|
+
};
|
|
67
|
+
for (let key of Object.keys(this).filter((k) => k.startsWith("attribute-"))) {
|
|
68
|
+
const attribute = this[key];
|
|
69
|
+
if (attribute) {
|
|
70
|
+
attributes[key.replace("attribute-", "")] = attribute;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
$.export("Defined attributes", attributes);
|
|
74
|
+
|
|
75
|
+
if (contact) {
|
|
76
|
+
identifier = contact.id;
|
|
77
|
+
const unlinkListIds = contact.listIds.filter((el) => !listIds.includes(el));
|
|
78
|
+
await this.brevo.updateContact(
|
|
79
|
+
$,
|
|
80
|
+
identifier,
|
|
81
|
+
attributes,
|
|
82
|
+
listIds,
|
|
83
|
+
unlinkListIds,
|
|
84
|
+
);
|
|
85
|
+
$.export("$summary", `Successfully updated contact "${identifier}"`);
|
|
86
|
+
} else {
|
|
87
|
+
const inserted = await this.brevo.addContact(
|
|
88
|
+
$,
|
|
89
|
+
attributes,
|
|
90
|
+
listIds,
|
|
91
|
+
);
|
|
92
|
+
identifier = inserted.id;
|
|
93
|
+
$.export("$summary", `Successfully inserted contact "${identifier}"`);
|
|
94
|
+
}
|
|
95
|
+
return this.brevo.existingContactByIdentifier(
|
|
96
|
+
$,
|
|
97
|
+
encodeURIComponent(identifier),
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import brevo from "../../brevo.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "brevo-send-transactional-email",
|
|
5
|
+
name: "Send Transactional Email",
|
|
6
|
+
description: "Send transactional email. [See the docs](https://developers.brevo.com/reference/sendtransacemail) for more information.",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
props: {
|
|
10
|
+
brevo,
|
|
11
|
+
useTemplate: {
|
|
12
|
+
type: "boolean",
|
|
13
|
+
label: "Use Template",
|
|
14
|
+
description: "Should use template?",
|
|
15
|
+
reloadProps: true,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
async additionalProps() {
|
|
19
|
+
const dynamicProps = {};
|
|
20
|
+
if (this.useTemplate) {
|
|
21
|
+
dynamicProps.templateId = {
|
|
22
|
+
label: "Template Id",
|
|
23
|
+
type: "integer",
|
|
24
|
+
min: 0,
|
|
25
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
26
|
+
description: "Id of the template.",
|
|
27
|
+
options: async ({ prevContext }) => {
|
|
28
|
+
return this.brevo.getTemplatesPaginated(prevContext);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
} else {
|
|
32
|
+
dynamicProps.sender = {
|
|
33
|
+
type: "string",
|
|
34
|
+
label: "Sender",
|
|
35
|
+
description: "Pass name (optional) and email or id of sender from which emails will be sent.\nName will be ignored if passed along with sender id.\n**Example:** `{ \"name\": \"John Doe\", \"email\": \"john@doe.com\" }` or `{ \"id\": 1 }`",
|
|
36
|
+
options: async ({ prevContext }) => {
|
|
37
|
+
return this.brevo.getSendersFormattedOptions(prevContext);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
dynamicProps.subject = {
|
|
41
|
+
type: "string",
|
|
42
|
+
label: "Subject",
|
|
43
|
+
description: "Subject of the message.",
|
|
44
|
+
};
|
|
45
|
+
dynamicProps.htmlContent = {
|
|
46
|
+
type: "string",
|
|
47
|
+
label: "Html Content",
|
|
48
|
+
description: "HTML body of the message.",
|
|
49
|
+
};
|
|
50
|
+
dynamicProps.textContent = {
|
|
51
|
+
type: "string",
|
|
52
|
+
label: "Text Content",
|
|
53
|
+
optional: true,
|
|
54
|
+
description: "Plain Text body of the message.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const props = {
|
|
58
|
+
...dynamicProps,
|
|
59
|
+
replyTo: {
|
|
60
|
+
type: "string",
|
|
61
|
+
label: "Reply To",
|
|
62
|
+
optional: false,
|
|
63
|
+
description: "Email (required), along with name (optional), on which transactional mail recipients will be able to reply back.\n\n**Example:** `{ \"name\": \"John Doe\", \"email\": \"john@doe.com\" }`",
|
|
64
|
+
options: async ({ prevContext }) => {
|
|
65
|
+
return this.brevo.getSendersFormattedOptions(prevContext);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
to: {
|
|
69
|
+
type: "string[]",
|
|
70
|
+
label: "To",
|
|
71
|
+
description: "List of email addresses and names (optional) of the recipients.\n\n**Example:** `[{ \"name\": \"John Doe\", \"email\": \"john@doe.com\" }, { \"name\": \"Jane Doe\", \"email\": \"jane@doe.com\" }]`",
|
|
72
|
+
optional: false,
|
|
73
|
+
options: async ({ prevContext }) => {
|
|
74
|
+
return this.brevo.getContactsPaginated(prevContext, true);
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
cc: {
|
|
78
|
+
type: "string[]",
|
|
79
|
+
label: "CC",
|
|
80
|
+
optional: true,
|
|
81
|
+
description: "List of email addresses and names (optional) of the recipients in cc.\n\n**Example:** `[{ \"name\": \"John Doe\", \"email\": \"john@doe.com\" }, { \"name\": \"Jane Doe\", \"email\": \"jane@doe.com\" }]`",
|
|
82
|
+
options: async ({ prevContext }) => {
|
|
83
|
+
return this.brevo.getContactsPaginated(prevContext, true);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
bcc: {
|
|
87
|
+
type: "string[]",
|
|
88
|
+
label: "BCC",
|
|
89
|
+
optional: true,
|
|
90
|
+
description: "List of email addresses and names (optional) of the recipients in bcc.\n\n**Example:** `[{ \"name\": \"John Doe\", \"email\": \"john@doe.com\" }, { \"name\": \"Jane Doe\", \"email\": \"jane@doe.com\" }]`",
|
|
91
|
+
options: async ({ prevContext }) => {
|
|
92
|
+
return this.brevo.getContactsPaginated(prevContext, true);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
tags: {
|
|
96
|
+
type: "string[]",
|
|
97
|
+
label: "Tags",
|
|
98
|
+
optional: true,
|
|
99
|
+
description: "Tag your emails to find them more easily.",
|
|
100
|
+
},
|
|
101
|
+
params: {
|
|
102
|
+
type: "string",
|
|
103
|
+
label: "Params",
|
|
104
|
+
description: "All key-value properties that will be replaced in the template e.g. `{\"ORDER\": 12345, \"DATE\": \"12/06/2019\"}`",
|
|
105
|
+
optional: true,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return props;
|
|
110
|
+
},
|
|
111
|
+
methods: {
|
|
112
|
+
formatEmailProp(prop, field) {
|
|
113
|
+
if (typeof (prop) === "string") {
|
|
114
|
+
prop = JSON.parse(prop);
|
|
115
|
+
}
|
|
116
|
+
if (!Array.isArray(prop)) {
|
|
117
|
+
throw new Error(`Field "${field}" should be an array`, prop);
|
|
118
|
+
}
|
|
119
|
+
if (typeof prop[0] === "string") {
|
|
120
|
+
return Object.keys(prop).map((key) => JSON.parse(prop[key]));
|
|
121
|
+
}
|
|
122
|
+
return prop;
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
async run({ $ }) {
|
|
126
|
+
const sender = this.sender ?
|
|
127
|
+
JSON.parse(this.sender) :
|
|
128
|
+
null;
|
|
129
|
+
const replyTo = this.replyTo ?
|
|
130
|
+
JSON.parse(this.replyTo) :
|
|
131
|
+
null;
|
|
132
|
+
const params = this.params ?
|
|
133
|
+
JSON.parse(this.params) :
|
|
134
|
+
null;
|
|
135
|
+
|
|
136
|
+
const tags = this.tags ?
|
|
137
|
+
Object.keys(this.tags).map((key) => this.tags[key])
|
|
138
|
+
: null;
|
|
139
|
+
const to = this.to
|
|
140
|
+
? this.formatEmailProp(this.to, "To")
|
|
141
|
+
: null;
|
|
142
|
+
const cc = this.cc
|
|
143
|
+
? this.formatEmailProp(this.cc, "CC")
|
|
144
|
+
: null;
|
|
145
|
+
const bcc = this.bcc
|
|
146
|
+
? this.formatEmailProp(this.bcc, "BCC")
|
|
147
|
+
: null;
|
|
148
|
+
|
|
149
|
+
if (!Array.isArray(to) || to.length === 0) {
|
|
150
|
+
throw new Error("Must provide field \"To\".");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const emailSent = await this.brevo.sendTransactionalEmail(
|
|
154
|
+
$,
|
|
155
|
+
this.useTemplate,
|
|
156
|
+
this.templateId,
|
|
157
|
+
sender,
|
|
158
|
+
replyTo,
|
|
159
|
+
to,
|
|
160
|
+
this.subject,
|
|
161
|
+
this.htmlContent,
|
|
162
|
+
this.textContent,
|
|
163
|
+
tags,
|
|
164
|
+
cc,
|
|
165
|
+
bcc,
|
|
166
|
+
params,
|
|
167
|
+
);
|
|
168
|
+
$.export("$summary", "Transactional email successfully sent");
|
|
169
|
+
return emailSent;
|
|
170
|
+
},
|
|
171
|
+
};
|
package/brevo.app.mjs
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { axios } from "@pipedream/platform";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
type: "app",
|
|
5
|
+
app: "brevo",
|
|
6
|
+
propDefinitions: {},
|
|
7
|
+
methods: {
|
|
8
|
+
_getBaseUrl() {
|
|
9
|
+
return "https://api.brevo.com/v3";
|
|
10
|
+
},
|
|
11
|
+
_getHeaders() {
|
|
12
|
+
return {
|
|
13
|
+
"content-type": "application/json",
|
|
14
|
+
"api-key": `${this.$auth.api_key}`,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
_getRequestParams(opts = {}) {
|
|
18
|
+
return {
|
|
19
|
+
...opts,
|
|
20
|
+
url: this._getBaseUrl() + opts.path,
|
|
21
|
+
headers: this._getHeaders(),
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
async createHook(ctx = this, createWebhookData) {
|
|
25
|
+
return await axios(ctx, this._getRequestParams({
|
|
26
|
+
method: "POST",
|
|
27
|
+
path: "/webhooks",
|
|
28
|
+
data: createWebhookData,
|
|
29
|
+
}));
|
|
30
|
+
},
|
|
31
|
+
async deleteHook(ctx = this, hookId) {
|
|
32
|
+
return await axios(ctx, this._getRequestParams({
|
|
33
|
+
method: "DELETE",
|
|
34
|
+
path: `/webhooks/${hookId}`,
|
|
35
|
+
}));
|
|
36
|
+
},
|
|
37
|
+
async existingContactByIdentifier(ctx = this, identifier) {
|
|
38
|
+
try {
|
|
39
|
+
return await axios(ctx, this._getRequestParams({
|
|
40
|
+
method: "GET",
|
|
41
|
+
path: `/contacts/${identifier}`,
|
|
42
|
+
}));
|
|
43
|
+
} catch (exception) {
|
|
44
|
+
if (exception.status === 404) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
throw exception;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async getContactsPaginated(prevContext, serializedValue = false) {
|
|
51
|
+
const limit = 20;
|
|
52
|
+
const offset = prevContext?.total
|
|
53
|
+
? prevContext?.offset + limit
|
|
54
|
+
: 0;
|
|
55
|
+
const contactsLists = await this.getContacts(prevContext, limit, offset);
|
|
56
|
+
const options = contactsLists.contacts.map((element) => {
|
|
57
|
+
const elementSerializedValue = {
|
|
58
|
+
id: element.id,
|
|
59
|
+
email: element.email,
|
|
60
|
+
name: element.attributes.NAME || element.attributes.FIRSTNAME || element.attributes.FNAME || "contact",
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
label: element.email,
|
|
64
|
+
value: serializedValue ?
|
|
65
|
+
JSON.stringify(elementSerializedValue) :
|
|
66
|
+
element.id,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
options,
|
|
71
|
+
context: {
|
|
72
|
+
offset: offset,
|
|
73
|
+
total: contactsLists.count,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
async getContacts(ctx = this, limit, offset) {
|
|
78
|
+
try {
|
|
79
|
+
return await axios(ctx, this._getRequestParams({
|
|
80
|
+
method: "GET",
|
|
81
|
+
path: `/contacts?limit=${limit}&offset=${offset}`,
|
|
82
|
+
}));
|
|
83
|
+
} catch (exception) {
|
|
84
|
+
if (exception.status === 404) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
throw exception;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
async getContactAttributes(ctx = this) {
|
|
91
|
+
return await axios(ctx, this._getRequestParams({
|
|
92
|
+
method: "GET",
|
|
93
|
+
path: "/contacts/attributes",
|
|
94
|
+
}));
|
|
95
|
+
},
|
|
96
|
+
async getListsPaginated(prevContext) {
|
|
97
|
+
const limit = 20;
|
|
98
|
+
const offset = prevContext?.total
|
|
99
|
+
? prevContext?.offset + limit
|
|
100
|
+
: 0;
|
|
101
|
+
const emailLists = await this.getLists(prevContext, limit, offset);
|
|
102
|
+
const options = emailLists.lists.map((element) => {
|
|
103
|
+
return {
|
|
104
|
+
label: element.name,
|
|
105
|
+
value: element.id,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
options,
|
|
110
|
+
context: {
|
|
111
|
+
offset: offset,
|
|
112
|
+
total: emailLists.count,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
async getLists(ctx = this, limit, offset) {
|
|
117
|
+
return await axios(ctx, this._getRequestParams({
|
|
118
|
+
method: "GET",
|
|
119
|
+
path: `/contacts/lists?limit=${limit}&offset=${offset}&sort=desc`,
|
|
120
|
+
}));
|
|
121
|
+
},
|
|
122
|
+
async getSendersFormattedOptions(prevContext) {
|
|
123
|
+
const sendersList = await this.getSenders(prevContext);
|
|
124
|
+
const options = sendersList.senders.map((element) => {
|
|
125
|
+
return {
|
|
126
|
+
label: element.name,
|
|
127
|
+
value: JSON.stringify({
|
|
128
|
+
name: element.name,
|
|
129
|
+
email: element.email,
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
options,
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
async getSenders(ctx = this) {
|
|
138
|
+
return await axios(ctx, this._getRequestParams({
|
|
139
|
+
method: "GET",
|
|
140
|
+
path: "/senders",
|
|
141
|
+
}));
|
|
142
|
+
},
|
|
143
|
+
async getTemplatesPaginated(prevContext) {
|
|
144
|
+
const limit = 20;
|
|
145
|
+
const offset = prevContext?.total
|
|
146
|
+
? prevContext?.offset + limit
|
|
147
|
+
: 0;
|
|
148
|
+
const templatesList = await this.getTemplates(prevContext, limit, offset);
|
|
149
|
+
const options = templatesList.templates.map((element) => {
|
|
150
|
+
return {
|
|
151
|
+
label: element.name,
|
|
152
|
+
value: element.id,
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
options,
|
|
157
|
+
context: {
|
|
158
|
+
offset: offset,
|
|
159
|
+
total: templatesList.count,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
async getTemplates(ctx = this, limit, offset) {
|
|
164
|
+
return await axios(ctx, this._getRequestParams({
|
|
165
|
+
method: "GET",
|
|
166
|
+
path: `/smtp/templates?limit=${limit}&offset=${offset}&sort=desc`,
|
|
167
|
+
}));
|
|
168
|
+
},
|
|
169
|
+
async addContact(ctx = this, attributes, listIds) {
|
|
170
|
+
const newContactData = {
|
|
171
|
+
email: attributes.EMAIL,
|
|
172
|
+
attributes,
|
|
173
|
+
};
|
|
174
|
+
if (listIds) {
|
|
175
|
+
newContactData.listIds = listIds;
|
|
176
|
+
}
|
|
177
|
+
return axios(ctx, this._getRequestParams({
|
|
178
|
+
method: "POST",
|
|
179
|
+
path: "/contacts",
|
|
180
|
+
data: newContactData,
|
|
181
|
+
}));
|
|
182
|
+
},
|
|
183
|
+
async updateContact(ctx = this, identifier, attributes, listIds, unlinkListIds) {
|
|
184
|
+
const updateContactData = {
|
|
185
|
+
attributes,
|
|
186
|
+
};
|
|
187
|
+
if (listIds) {
|
|
188
|
+
updateContactData.listIds = listIds;
|
|
189
|
+
}
|
|
190
|
+
if (unlinkListIds) {
|
|
191
|
+
updateContactData.unlinkListIds = unlinkListIds;
|
|
192
|
+
}
|
|
193
|
+
return axios(ctx, this._getRequestParams({
|
|
194
|
+
method: "PUT",
|
|
195
|
+
path: `/contacts/${identifier}`,
|
|
196
|
+
data: updateContactData,
|
|
197
|
+
}));
|
|
198
|
+
},
|
|
199
|
+
async sendTransactionalEmail(
|
|
200
|
+
ctx = this,
|
|
201
|
+
useTemplate,
|
|
202
|
+
templateId,
|
|
203
|
+
sender,
|
|
204
|
+
replyTo,
|
|
205
|
+
to,
|
|
206
|
+
subject,
|
|
207
|
+
htmlContent,
|
|
208
|
+
textContent,
|
|
209
|
+
tags,
|
|
210
|
+
cc,
|
|
211
|
+
bcc,
|
|
212
|
+
params,
|
|
213
|
+
) {
|
|
214
|
+
const emailData = {
|
|
215
|
+
to,
|
|
216
|
+
replyTo,
|
|
217
|
+
};
|
|
218
|
+
if (useTemplate) {
|
|
219
|
+
emailData.templateId = templateId;
|
|
220
|
+
} else {
|
|
221
|
+
emailData.sender = sender;
|
|
222
|
+
emailData.subject = subject;
|
|
223
|
+
emailData.htmlContent = htmlContent;
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(tags) && tags.length > 0) {
|
|
226
|
+
emailData.tags = tags;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(cc) && cc.length > 0) {
|
|
229
|
+
emailData.cc = cc;
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(bcc) && bcc.length > 0) {
|
|
232
|
+
emailData.bcc = bcc;
|
|
233
|
+
}
|
|
234
|
+
if (textContent) {
|
|
235
|
+
emailData.textContent = textContent;
|
|
236
|
+
}
|
|
237
|
+
if (params) {
|
|
238
|
+
emailData.params = params;
|
|
239
|
+
}
|
|
240
|
+
return axios(ctx, this._getRequestParams({
|
|
241
|
+
method: "POST",
|
|
242
|
+
path: "/smtp/email",
|
|
243
|
+
data: emailData,
|
|
244
|
+
}));
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
marketingEventOptions: [
|
|
3
|
+
"spam",
|
|
4
|
+
"opened",
|
|
5
|
+
"click",
|
|
6
|
+
"hardBounce",
|
|
7
|
+
"softBounce",
|
|
8
|
+
"unsubscribed",
|
|
9
|
+
"listAddition",
|
|
10
|
+
"delivered",
|
|
11
|
+
],
|
|
12
|
+
transactionalEventOptions: [
|
|
13
|
+
"sent",
|
|
14
|
+
"delivered",
|
|
15
|
+
"hardBounce",
|
|
16
|
+
"softBounce",
|
|
17
|
+
"blocked",
|
|
18
|
+
"spam",
|
|
19
|
+
"invalid",
|
|
20
|
+
"deferred",
|
|
21
|
+
"click",
|
|
22
|
+
"opened",
|
|
23
|
+
"uniqueOpened",
|
|
24
|
+
"unsubscribed",
|
|
25
|
+
],
|
|
26
|
+
};
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/brevo",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Pipedream Brevo Components",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "brevo.app.mjs",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pipedream",
|
|
8
8
|
"brevo"
|
|
9
9
|
],
|
|
10
|
-
"files": [
|
|
11
|
-
"dist"
|
|
12
|
-
],
|
|
13
10
|
"homepage": "https://pipedream.com/apps/brevo",
|
|
14
11
|
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
|
|
15
12
|
"publishConfig": {
|
|
16
13
|
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@pipedream/platform": "^1.5.1"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import brevo from "../brevo.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
props: {
|
|
5
|
+
brevo,
|
|
6
|
+
http: "$.interface.http",
|
|
7
|
+
db: "$.service.db",
|
|
8
|
+
},
|
|
9
|
+
methods: {
|
|
10
|
+
emitEvent(body) {
|
|
11
|
+
const meta = this.generateMeta(body);
|
|
12
|
+
this.$emit(meta, {
|
|
13
|
+
id: meta.ts,
|
|
14
|
+
summary: meta.event,
|
|
15
|
+
ts: meta.ts,
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
setHookId(hookId) {
|
|
19
|
+
this.db.set("hookId", hookId);
|
|
20
|
+
},
|
|
21
|
+
getHookId() {
|
|
22
|
+
return this.db.get("hookId");
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
hooks: {
|
|
26
|
+
async activate() {
|
|
27
|
+
const eventNames = this.getEventNames();
|
|
28
|
+
const description = this.getHookDescription();
|
|
29
|
+
const type = this.getEventType();
|
|
30
|
+
const createHookData = {
|
|
31
|
+
description: description,
|
|
32
|
+
url: this.http.endpoint,
|
|
33
|
+
events: eventNames,
|
|
34
|
+
type: type,
|
|
35
|
+
};
|
|
36
|
+
const { id } = await this.brevo.createHook(this, createHookData);
|
|
37
|
+
this.setHookId(id);
|
|
38
|
+
},
|
|
39
|
+
async deactivate() {
|
|
40
|
+
const hookId = this.getHookId();
|
|
41
|
+
await this.brevo.deleteHook(this, hookId);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
async run(event) {
|
|
45
|
+
const { body } = event;
|
|
46
|
+
const hookId = this.getHookId();
|
|
47
|
+
if (body.id !== hookId) {
|
|
48
|
+
throw new Error("The request was aborted: registered and requested webhook's id doesn't match.");
|
|
49
|
+
}
|
|
50
|
+
this.emitEvent(body);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import options from "../../common/options.mjs";
|
|
2
|
+
import common from "../common-webhook.mjs";
|
|
3
|
+
import sampleEmit from "./test-event.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
...common,
|
|
7
|
+
key: "brevo-marketing-webhook",
|
|
8
|
+
name: "New Marketing Webhook (Instant)",
|
|
9
|
+
description: "Emit new event when triggered by a marketing event",
|
|
10
|
+
version: "0.0.1",
|
|
11
|
+
type: "source",
|
|
12
|
+
dedupe: "unique",
|
|
13
|
+
props: {
|
|
14
|
+
...common.props,
|
|
15
|
+
description: {
|
|
16
|
+
type: "string",
|
|
17
|
+
label: "Description",
|
|
18
|
+
description: "Description of the webhook.",
|
|
19
|
+
},
|
|
20
|
+
events: {
|
|
21
|
+
type: "string[]",
|
|
22
|
+
label: "Events",
|
|
23
|
+
description: "Events triggering the webhook.",
|
|
24
|
+
options: options.marketingEventOptions,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
methods: {
|
|
28
|
+
...common.methods,
|
|
29
|
+
getEventNames() {
|
|
30
|
+
return this.events;
|
|
31
|
+
},
|
|
32
|
+
getHookDescription() {
|
|
33
|
+
return this.description;
|
|
34
|
+
},
|
|
35
|
+
getEventType() {
|
|
36
|
+
return "marketing";
|
|
37
|
+
},
|
|
38
|
+
generateMeta(body) {
|
|
39
|
+
const meta = {
|
|
40
|
+
...body,
|
|
41
|
+
};
|
|
42
|
+
return meta;
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
sampleEmit,
|
|
46
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"id": 123,
|
|
3
|
+
"email": "email@test.com",
|
|
4
|
+
"message-id": "<2017962224.4320228221195@smtp-relay.mailin.fr>",
|
|
5
|
+
"date": "2024-01-20 12:17:34",
|
|
6
|
+
"tags": [
|
|
7
|
+
"tagTest",
|
|
8
|
+
],
|
|
9
|
+
"tag": "[\"tagTest\"]",
|
|
10
|
+
"event": "request",
|
|
11
|
+
"subject": "You have been unsubscribed successfully",
|
|
12
|
+
"ts_event": 123456789,
|
|
13
|
+
"ts": 123456789,
|
|
14
|
+
"template_id": 1,
|
|
15
|
+
"reason": "sent",
|
|
16
|
+
"ts_epoch": 123456789,
|
|
17
|
+
"sender_email": "sender@email.com"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"id": 123,
|
|
3
|
+
"email": "email@test.com",
|
|
4
|
+
"message-id": "<2017962224.4320228221195@smtp-relay.mailin.fr>",
|
|
5
|
+
"date": "2024-01-20 12:17:34",
|
|
6
|
+
"tags": [
|
|
7
|
+
"tagTest",
|
|
8
|
+
],
|
|
9
|
+
"tag": "[\"tagTest\"]",
|
|
10
|
+
"event": "request",
|
|
11
|
+
"subject": "You have been unsubscribed successfully",
|
|
12
|
+
"ts_event": 123456789,
|
|
13
|
+
"ts": 123456789,
|
|
14
|
+
"template_id": 1,
|
|
15
|
+
"reason": "sent",
|
|
16
|
+
"ts_epoch": 123456789,
|
|
17
|
+
"sender_email": "sender@email.com"
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import options from "../../common/options.mjs";
|
|
2
|
+
import common from "../common-webhook.mjs";
|
|
3
|
+
import sampleEmit from "./test-event.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
...common,
|
|
7
|
+
key: "brevo-transactional-webhook",
|
|
8
|
+
name: "New Transactional Webhook (Instant)",
|
|
9
|
+
description: "Emit new event when triggered by a transactional event",
|
|
10
|
+
version: "0.0.1",
|
|
11
|
+
type: "source",
|
|
12
|
+
dedupe: "unique",
|
|
13
|
+
props: {
|
|
14
|
+
...common.props,
|
|
15
|
+
description: {
|
|
16
|
+
type: "string",
|
|
17
|
+
label: "Description",
|
|
18
|
+
description: "Description of the webhook.",
|
|
19
|
+
},
|
|
20
|
+
events: {
|
|
21
|
+
type: "string[]",
|
|
22
|
+
label: "Events",
|
|
23
|
+
description: "Events triggering the webhook.",
|
|
24
|
+
options: options.transactionalEventOptions,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
methods: {
|
|
28
|
+
...common.methods,
|
|
29
|
+
getEventNames() {
|
|
30
|
+
return this.events;
|
|
31
|
+
},
|
|
32
|
+
getHookDescription() {
|
|
33
|
+
return this.description;
|
|
34
|
+
},
|
|
35
|
+
getEventType() {
|
|
36
|
+
return "transactional";
|
|
37
|
+
},
|
|
38
|
+
generateMeta(body) {
|
|
39
|
+
const meta = {
|
|
40
|
+
...body,
|
|
41
|
+
};
|
|
42
|
+
return meta;
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
sampleEmit,
|
|
46
|
+
};
|