Skip to content

Commit d0fc909

Browse files
authored
13291 components unione (#19051)
* Update UniOne component: bump version to 0.1.0, add dependencies, implement email sending action, and introduce new constants and utility functions. * pnpm update * Refactor email action parameters to use boolean types instead of strings, and remove unused constants for skip unsubscribe and tracking options. Update descriptions for clarity and ensure proper parsing of boolean values in the email sending logic. * Refactor email action to use boolean for skipUnsubscribe, removing unused constants and ensuring correct parsing of boolean values in the email sending logic. * Refactor email action to consistently use boolean values for tracking and bypass options, ensuring proper conversion to integers for the email sending logic.
1 parent 6fc78a7 commit d0fc909

File tree

6 files changed

+423
-7
lines changed

6 files changed

+423
-7
lines changed
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import {
2+
GLOBAL_LANGUAGE_OPTIONS,
3+
TEMPLATE_ENGINE_OPTIONS,
4+
} from "../../common/constants.mjs";
5+
import { parseObject } from "../../common/utils.mjs";
6+
import app from "../../unione.app.mjs";
7+
8+
export default {
9+
key: "unione-send-email",
10+
name: "Send Email",
11+
description: "Send an email using UniOne. [See the documentation](https://docs.unione.io/en/web-api-ref#email-send)",
12+
version: "0.0.1",
13+
annotations: {
14+
destructiveHint: false,
15+
openWorldHint: true,
16+
readOnlyHint: false,
17+
},
18+
type: "action",
19+
props: {
20+
app,
21+
recipients: {
22+
type: "string[]",
23+
label: "Recipients",
24+
description: "Array of recipient objects with email, substitutions (merge tags), and metadata. Each recipient can have: email (required), substitutions (object, optional), metadata (object, optional). If provided, this takes precedence over 'To' prop. Example: [{ \"email\": \"recipient@example.com\", \"substitutions\": { \"from_name\": \"John Doe\", \"subject\": \"Hello, {name}!\" }, \"metadata\": { \"company\": \"Example Inc.\" } }]. [See the documentation](https://docs.unione.io/en/web-api-ref#email-send)",
25+
optional: true,
26+
},
27+
templateId: {
28+
propDefinition: [
29+
app,
30+
"templateId",
31+
],
32+
optional: true,
33+
},
34+
tags: {
35+
propDefinition: [
36+
app,
37+
"tags",
38+
],
39+
optional: true,
40+
},
41+
skipUnsubscribe: {
42+
type: "boolean",
43+
label: "Skip Unsubscribe",
44+
description: "Whether to skip or not appending default unsubscribe footer. You should [ask support](https://cp.unione.io/en/support?_gl=1*1afrczd*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGwwJGg4ODQyOTkzMzQ.) to approve.",
45+
optional: true,
46+
},
47+
globalLanguage: {
48+
type: "string",
49+
label: "Global Language",
50+
description: "The language of the unsubscribe footer and unsubscribe page.",
51+
options: GLOBAL_LANGUAGE_OPTIONS,
52+
optional: true,
53+
},
54+
templateEngine: {
55+
type: "string",
56+
label: "Template Engine",
57+
description: "The [template engine](https://docs.unione.io/en/template-engines) for handling the substitutions(merge tags).",
58+
optional: true,
59+
options: TEMPLATE_ENGINE_OPTIONS,
60+
},
61+
globalSubstitutions: {
62+
type: "object",
63+
label: "Global Substitutions",
64+
description: "Object for passing the substitutions(merge tags) common for all recipients - e.g., company name. If the substitution names are duplicated in recipient 'substitutions', the values of the variables will be taken from the recipient 'substitutions'. Example: { \"body\": { \"html\": \"Hello, {name}!\", \"plaintext\": \"Hello, {name}!\", \"amp\": \"Hello, {name}!\"}, \"subject\": \"Hello, {name}!\", \"from_name\": \"John Doe\", \"options\": { \"unsubscribe_url\": \"https://example.com/unsubscribe\" } }.",
65+
optional: true,
66+
},
67+
globalMetadata: {
68+
type: "object",
69+
label: "Global Metadata",
70+
description: "Object for passing the metadata common for all the recipients, such as 'key': 'value'. Max key quantity: 10. Max key length: 64 symbols. Max value length: 1024 symbols.",
71+
optional: true,
72+
},
73+
body: {
74+
type: "object",
75+
label: "Body",
76+
description: "Contains HTML/plaintext/AMP parts of the email. Either html or plaintext part is required. Example: { \"html\": \"Hello, {name}!\", \"plaintext\": \"Hello, {name}!\", \"amp\": \"Hello, {name}!\" }.",
77+
},
78+
subject: {
79+
type: "string",
80+
label: "Subject",
81+
description: "Email subject",
82+
},
83+
fromEmail: {
84+
type: "string",
85+
label: "From Email",
86+
description: "Sender's email. Required only if `Template ID` prop is empty.",
87+
optional: true,
88+
},
89+
fromName: {
90+
type: "string",
91+
label: "From Name",
92+
description: "Sender's name",
93+
optional: true,
94+
},
95+
replyTo: {
96+
type: "string",
97+
label: "Reply To",
98+
description: "Reply-to email (in case it's different to sender's email)",
99+
optional: true,
100+
},
101+
replyToName: {
102+
type: "string",
103+
label: "Reply To Name",
104+
description: "Reply-To name (if `Reply To` email is specified and you want to display not only this email but also the name)",
105+
optional: true,
106+
},
107+
trackLinks: {
108+
type: "boolean",
109+
label: "Track Links",
110+
description: "If true, click tracking is on (default). If false, click tracking is off. To use track_links = false, you need to ask UniOne support to enable this feature.",
111+
optional: true,
112+
},
113+
trackRead: {
114+
type: "boolean",
115+
label: "Track Read",
116+
description: "If true, read tracking is on (default). If false, read tracking is off. To use track_read = false, you need to ask support to enable this feature.",
117+
optional: true,
118+
},
119+
bypassGlobal: {
120+
type: "boolean",
121+
label: "Bypass Global",
122+
description: "If true, the global unavailability list will be ignored. Even if the address was found to be unreachable while sending other UniOne users' emails, or its owner has issued complaints, the message will still be sent. The setting may be ignored for certain addresses.",
123+
optional: true,
124+
},
125+
bypassUnavailable: {
126+
type: "boolean",
127+
label: "Bypass Unavailable",
128+
description: "If true, the current list of unsubscribed addresses for this account or project will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link (to request, please contact [support](https://cp.unione.io/en/support?_gl=1*1msqb8a*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGg4ODQyOTkzMzQ.)).",
129+
optional: true,
130+
},
131+
bypassUnsubscribed: {
132+
type: "boolean",
133+
label: "Bypass Unsubscribed",
134+
description: "If true, the current list of unsubscribed addresses for this account or project will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link.",
135+
optional: true,
136+
},
137+
bypassComplained: {
138+
type: "boolean",
139+
label: "Bypass Complained",
140+
description: "If true, the user's or project's complaint list will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link.",
141+
optional: true,
142+
},
143+
idempotenceKey: {
144+
type: "string",
145+
label: "Idempotence Key",
146+
description: "A string of up to 64 characters containing a unique message key. This can be used to prevent occasional message duplicates. If you send another API request with the same message key within the next minute, it will be declined. We can generate a message key for each letter automatically; to enable this option, please contact our [tech support](https://cp.unione.io/en/support?_gl=1*dahmdm*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGwwJGg4ODQyOTkzMzQ.).",
147+
optional: true,
148+
},
149+
headers: {
150+
type: "object",
151+
label: "Headers",
152+
description: "Contains email headers, maximum 50. Only headers with “X-” name prefix are accepted, all other are ignored, for example X-UNIONE-Global-Language, X-UNIONE-Template-Engine. Standard headers “To,” “CC,” and “BCC” are passed without the “X-.” Yet, they are processed in a particular way and, as a result, have a number of restrictions. You can find more details about it [here](https://docs.unione.io/cc-and-bcc). If our support have approved omitting standard unsubscription block for you, you can also pass List-Unsubscribe, List-Subscribe, List-Help, List-Owner, List-Archive, In-Reply-To and References headers. Example: { \"X-UNIONE-Global-Language\": \"en\", \"X-UNIONE-Template-Engine\": \"velocity\" }.",
153+
optional: true,
154+
},
155+
sendAt: {
156+
type: "string",
157+
label: "Send At",
158+
description: "Date and time in 'YYYY-MM-DD hh:mm:ss' format in the UTC timezone. Allows schedule sending up to 24 hours in advance.",
159+
optional: true,
160+
},
161+
unsubscribeUrl: {
162+
type: "string",
163+
label: "Unsubscribe URL",
164+
description: "Custom unsubscribe link. Read more [here](https://docs.unione.io/en/unsubscribe-link).",
165+
optional: true,
166+
},
167+
},
168+
async run({ $ }) {
169+
if (!this.templateId && !this.fromEmail) {
170+
throw new Error("`From Email` is required when `Template ID` prop is not provided");
171+
}
172+
173+
const response = await this.app.sendEmail({
174+
$,
175+
data: {
176+
message: {
177+
recipients: parseObject(this.recipients),
178+
template_id: this.templateId,
179+
tags: parseObject(this.tags),
180+
skip_unsubscribe: this.skipUnsubscribe
181+
? 1
182+
: 0,
183+
global_language: parseObject(this.globalLanguage),
184+
template_engine: parseObject(this.templateEngine),
185+
global_substitutions: parseObject(this.globalSubstitutions),
186+
global_metadata: parseObject(this.globalMetadata),
187+
body: this.body && parseObject(this.body),
188+
subject: this.subject,
189+
from_email: this.fromEmail,
190+
from_name: this.fromName,
191+
reply_to: this.replyTo,
192+
reply_to_name: this.replyToName,
193+
track_links: this.trackLinks
194+
? 1
195+
: 0,
196+
track_read: this.trackRead
197+
? 1
198+
: 0,
199+
bypass_global: this.bypassGlobal
200+
? 1
201+
: 0,
202+
bypass_unavailable: this.bypassUnavailable
203+
? 1
204+
: 0,
205+
bypass_unsubscribed: this.bypassUnsubscribed
206+
? 1
207+
: 0,
208+
bypass_complained: this.bypassComplained
209+
? 1
210+
: 0,
211+
idempotence_key: this.idempotenceKey,
212+
headers: this.headers && parseObject(this.headers),
213+
options: {
214+
send_at: this.sendAt,
215+
unsubscribe_url: this.unsubscribeUrl,
216+
},
217+
},
218+
},
219+
});
220+
221+
const recipientEmails = parseObject(this.recipients).map((r) => r.email || r)
222+
.join(", ");
223+
if (response.status === "success") {
224+
$.export("$summary", `Successfully sent email to ${recipientEmails}`);
225+
} else {
226+
$.export("$summary", `Email send request completed with status: ${response.status}`);
227+
}
228+
229+
return response;
230+
},
231+
};
232+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
export const LIMIT = 100;
2+
3+
export const GLOBAL_LANGUAGE_OPTIONS = [
4+
{
5+
label: "Belarusian",
6+
value: "be",
7+
},
8+
{
9+
label: "German",
10+
value: "de",
11+
},
12+
{
13+
label: "English",
14+
value: "en",
15+
},
16+
{
17+
label: "Spanish",
18+
value: "es",
19+
},
20+
{
21+
label: "French",
22+
value: "fr",
23+
},
24+
{
25+
label: "Italian",
26+
value: "it",
27+
},
28+
{
29+
label: "Polish",
30+
value: "pl",
31+
},
32+
{
33+
label: "Portuguese",
34+
value: "pt",
35+
},
36+
{
37+
label: "Russian",
38+
value: "ru",
39+
},
40+
{
41+
label: "Ukrainian",
42+
value: "ua",
43+
},
44+
{
45+
label: "Kazakh",
46+
value: "kz",
47+
},
48+
];
49+
50+
export const TEMPLATE_ENGINE_OPTIONS = [
51+
{
52+
label: "Simple",
53+
value: "simple",
54+
},
55+
{
56+
label: "Velocity",
57+
value: "velocity",
58+
},
59+
{
60+
label: "Liquid",
61+
value: "liquid",
62+
},
63+
{
64+
label: "None",
65+
value: "none",
66+
},
67+
];

components/unione/common/utils.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export const parseObject = (obj) => {
2+
if (!obj) return undefined;
3+
4+
if (Array.isArray(obj)) {
5+
return obj.map((item) => {
6+
if (typeof item === "string") {
7+
try {
8+
return JSON.parse(item);
9+
} catch (e) {
10+
return item;
11+
}
12+
}
13+
return item;
14+
});
15+
}
16+
if (typeof obj === "string") {
17+
try {
18+
return JSON.parse(obj);
19+
} catch (e) {
20+
return obj;
21+
}
22+
}
23+
return obj;
24+
};

components/unione/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/unione",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Pipedream UniOne Components",
55
"main": "unione.app.mjs",
66
"keywords": [
@@ -11,5 +11,8 @@
1111
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "^3.1.1"
1417
}
1518
}

0 commit comments

Comments
 (0)