Skip to content

Commit cb1f43b

Browse files
New Components - ortto (#14703)
* ortto init * [Components] ortto #13209 Sources - New Contact Created Actions - Create Custom Activity - Opt Out SMS * pnpm update * fix dependency import * pnpm update * some adjusts * Improve props description * Improve prop description --------- Co-authored-by: Leo Vu <[email protected]>
1 parent 1b5cda9 commit cb1f43b

File tree

13 files changed

+486
-20
lines changed

13 files changed

+486
-20
lines changed

components/ortto/.gitignore

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { parseObject } from "../../common/utils.mjs";
2+
import ortto from "../../ortto.app.mjs";
3+
4+
export default {
5+
key: "ortto-create-custom-activity",
6+
name: "Create Custom Activity",
7+
description: "Creates a unique activity for a person. Can optionally initialize a new record beforehand. [See the documentation](https://help.ortto.com/a-271-create-a-custom-activity-event-create)",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
ortto,
12+
activityId: {
13+
propDefinition: [
14+
ortto,
15+
"activityId",
16+
],
17+
},
18+
attributes: {
19+
propDefinition: [
20+
ortto,
21+
"attributes",
22+
],
23+
},
24+
fields: {
25+
propDefinition: [
26+
ortto,
27+
"fields",
28+
],
29+
},
30+
},
31+
async run({ $ }) {
32+
const response = await this.ortto.createCustomActivity({
33+
$,
34+
data: {
35+
"activities": [
36+
{
37+
activity_id: this.activityId,
38+
attributes: parseObject(this.attributes),
39+
fields: parseObject(this.fields),
40+
},
41+
],
42+
"merge_by": [
43+
"str::email",
44+
],
45+
},
46+
});
47+
48+
$.export("$summary", "Successfully created activity");
49+
return response;
50+
},
51+
};
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { clearObj } from "../../common/utils.mjs";
2+
import ortto from "../../ortto.app.mjs";
3+
4+
export default {
5+
key: "ortto-create-person",
6+
name: "Create or Update a Person",
7+
description: "Create or update a preexisting person in the Ortto account. [See the documentation](https://help.ortto.com/a-250-api-reference)",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
ortto,
12+
firstName: {
13+
type: "string",
14+
label: "First Name",
15+
description: "The person's first name.",
16+
optional: true,
17+
},
18+
lastName: {
19+
type: "string",
20+
label: "Last Name",
21+
description: "The person's last name.",
22+
optional: true,
23+
},
24+
phone: {
25+
type: "string",
26+
label: "Phone",
27+
description: "The person's phone number.",
28+
},
29+
email: {
30+
type: "string",
31+
label: "Email",
32+
description: "The person's email address.",
33+
},
34+
city: {
35+
type: "string",
36+
label: "City",
37+
description: "The person's address city.",
38+
optional: true,
39+
},
40+
country: {
41+
type: "string",
42+
label: "Country",
43+
description: "The person's address country.",
44+
optional: true,
45+
},
46+
region: {
47+
type: "string",
48+
label: "Region",
49+
description: "The person's address region.",
50+
optional: true,
51+
},
52+
birthday: {
53+
type: "string",
54+
label: "Birthday",
55+
description: "The person's birth date.",
56+
optional: true,
57+
},
58+
},
59+
async run({ $ }) {
60+
const {
61+
ortto,
62+
...props
63+
} = this;
64+
65+
const birthday = {};
66+
if (props.birthday) {
67+
const date = new Date(props.birthday);
68+
birthday.day = date.getDate();
69+
birthday.month = date.getMonth() + 1;
70+
birthday.year = date.getFullYear();
71+
}
72+
73+
const response = await ortto.createPerson({
74+
data: {
75+
people: [
76+
{
77+
fields: clearObj({
78+
"str::first": props.firstName,
79+
"str::last": props.lastName,
80+
"phn::phone": {
81+
"phone": props.phone,
82+
"parse_with_country_code": true,
83+
},
84+
"str::email": props.email,
85+
"geo::city": {
86+
name: props.city,
87+
},
88+
"geo::country": {
89+
name: props.country,
90+
},
91+
"geo::region": {
92+
name: props.region,
93+
},
94+
"dtz::b": birthday,
95+
}),
96+
},
97+
],
98+
async: false,
99+
merge_by: [
100+
"str::email",
101+
],
102+
find_strategy: 0,
103+
},
104+
});
105+
$.export("$summary", "Person successfully initialized or updated!");
106+
return response;
107+
},
108+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import ortto from "../../ortto.app.mjs";
2+
3+
export default {
4+
key: "ortto-opt-out-sms",
5+
name: "Opt Out of SMS",
6+
description: "Allows a user to opt-out from all SMS communications. [See the documentation](https://help.ortto.com/a-250-api-reference)",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
ortto,
11+
userEmail: {
12+
propDefinition: [
13+
ortto,
14+
"userEmail",
15+
],
16+
},
17+
},
18+
async run({ $ }) {
19+
const response = await this.ortto.updatePerson({
20+
data: {
21+
people: [
22+
{
23+
fields: {
24+
"str::email": this.userEmail,
25+
"bol::sp": false,
26+
},
27+
},
28+
],
29+
async: false,
30+
merge_by: [
31+
"str::email",
32+
],
33+
},
34+
});
35+
36+
$.export("$summary", `Successfully opted out SMS for User ID: ${this.userEmail}`);
37+
return response;
38+
},
39+
};

components/ortto/app/ortto.app.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

components/ortto/common/constants.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const LIMIT = 100;

components/ortto/common/utils.mjs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export const clearObj = (obj) => {
2+
return Object.entries(obj)
3+
.filter(([
4+
,
5+
v,
6+
]) => (v != null && v != "" && JSON.stringify(v) != "{}"))
7+
.reduce((acc, [
8+
k,
9+
v,
10+
]) => ({
11+
...acc,
12+
[k]: (!Array.isArray(v) && v === Object(v))
13+
? clearObj(v)
14+
: v,
15+
}), {});
16+
};
17+
18+
export const parseObject = (obj) => {
19+
if (!obj) return undefined;
20+
21+
if (Array.isArray(obj)) {
22+
return obj.map((item) => {
23+
if (typeof item === "string") {
24+
try {
25+
return JSON.parse(item);
26+
} catch (e) {
27+
return item;
28+
}
29+
}
30+
return item;
31+
});
32+
}
33+
if (typeof obj === "string") {
34+
try {
35+
return JSON.parse(obj);
36+
} catch (e) {
37+
return obj;
38+
}
39+
}
40+
return obj;
41+
};

components/ortto/ortto.app.mjs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { axios } from "@pipedream/platform";
2+
import { LIMIT } from "./common/constants.mjs";
3+
4+
export default {
5+
type: "app",
6+
app: "ortto",
7+
propDefinitions: {
8+
userEmail: {
9+
type: "string",
10+
label: "User Email",
11+
description: "Specify the user email to opt out from all SMS communications.",
12+
async options({ page }) {
13+
const { contacts } = await this.listPeople({
14+
data: {
15+
limit: LIMIT,
16+
offset: LIMIT * page,
17+
fields: [
18+
"str::first",
19+
"str::last",
20+
"str::email",
21+
],
22+
},
23+
});
24+
25+
return contacts.map(({ fields }) => ({
26+
label: `${fields["str::first"]} ${fields["str::last"]} (${fields["str::email"]})`,
27+
value: fields["str::email"],
28+
}));
29+
},
30+
},
31+
activityId: {
32+
type: "string",
33+
label: "Activity Id",
34+
description: "The Id of the activity definition. To find Activity ID, login into your Ortto app > CDP > Activities > Select an activity, then you can find the Activity ID in the browser URL as `https://ortto.app/{Org}/activities/{Activity_ID}/overview`. For example, if your Activity URL is `https://ortto.app/pipedreamtest/activities/act::s/overview`, then your Activity ID is `act::s`",
35+
},
36+
fields: {
37+
type: "object",
38+
label: "Fields",
39+
description: "The object containing the fields for a person associated with the event.",
40+
},
41+
attributes: {
42+
type: "object",
43+
label: "Attributes",
44+
description: "An object with the attributes. To find Activity attributes, login into your Ortto app > CDP > Activities > Select an activity > Developer.",
45+
},
46+
},
47+
methods: {
48+
_baseUrl() {
49+
return `https://${this.$auth.region}/v1`;
50+
},
51+
_headers() {
52+
return {
53+
"X-Api-Key": `${this.$auth.api_key}`,
54+
"Content-Type": "application/json",
55+
};
56+
},
57+
_makeRequest({
58+
$ = this, path, ...opts
59+
}) {
60+
return axios($, {
61+
method: "POST",
62+
url: this._baseUrl() + path,
63+
headers: this._headers(),
64+
...opts,
65+
});
66+
},
67+
listPeople(opts = {}) {
68+
return this._makeRequest({
69+
path: "/person/get",
70+
...opts,
71+
});
72+
},
73+
updatePerson(opts = {}) {
74+
return this._makeRequest({
75+
path: "/person/merge",
76+
...opts,
77+
});
78+
},
79+
createPerson(opts = {}) {
80+
return this._makeRequest({
81+
method: "POST",
82+
path: "/person/merge",
83+
...opts,
84+
});
85+
},
86+
createCustomActivity(opts = {}) {
87+
return this._makeRequest({
88+
method: "POST",
89+
path: "/activities/create",
90+
...opts,
91+
});
92+
},
93+
async *paginate({
94+
fn, data = {}, fieldName, ...opts
95+
}) {
96+
let hasMore = false;
97+
let page = 0;
98+
99+
do {
100+
data.limit = LIMIT;
101+
data.offset = LIMIT * page++;
102+
const response = await fn({
103+
data,
104+
...opts,
105+
});
106+
for (const d of response[fieldName]) {
107+
yield d;
108+
}
109+
110+
hasMore = response.has_more;
111+
112+
} while (hasMore);
113+
},
114+
},
115+
};

0 commit comments

Comments
 (0)