Skip to content

Commit cbbae66

Browse files
committed
rejoiner init
1 parent 3c2d4d6 commit cbbae66

File tree

6 files changed

+359
-2
lines changed

6 files changed

+359
-2
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import rejoiner from "../../rejoiner.app.mjs";
2+
import { axios } from "@pipedream/platform";
3+
4+
export default {
5+
key: "rejoiner-add-customer-to-list",
6+
name: "Add Customer to List",
7+
description: "Adds a customer to a specific list. [See the documentation](https://docs.rejoiner.com/docs/getting-started)",
8+
version: "0.0.{{ts}}",
9+
type: "action",
10+
props: {
11+
rejoiner,
12+
customerId: {
13+
propDefinition: [
14+
rejoiner,
15+
"customerId",
16+
],
17+
},
18+
listId: {
19+
propDefinition: [
20+
rejoiner,
21+
"listId",
22+
],
23+
},
24+
listSource: {
25+
propDefinition: [
26+
rejoiner,
27+
"listSource",
28+
],
29+
optional: true,
30+
},
31+
},
32+
async run({ $ }) {
33+
const response = await this.rejoiner.addCustomerToList();
34+
$.export("$summary", `Added customer ${this.customerId} to list ${this.listId}`);
35+
return response;
36+
},
37+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import rejoiner from "../../rejoiner.app.mjs";
2+
import { axios } from "@pipedream/platform";
3+
4+
export default {
5+
key: "rejoiner-start-journey",
6+
name: "Start Journey",
7+
description: "Triggers the beginning of a customer journey in Rejoiner. [See the documentation]()",
8+
version: "0.0.{{ts}}",
9+
type: "action",
10+
props: {
11+
rejoiner: {
12+
type: "app",
13+
app: "rejoiner",
14+
},
15+
journeyId: {
16+
propDefinition: [
17+
rejoiner,
18+
"journeyId",
19+
],
20+
},
21+
customerId: {
22+
propDefinition: [
23+
rejoiner,
24+
"customerId",
25+
],
26+
},
27+
metadata: {
28+
propDefinition: [
29+
rejoiner,
30+
"metadata",
31+
],
32+
optional: true,
33+
},
34+
},
35+
async run({ $ }) {
36+
this.rejoiner.journeyId = this.journeyId;
37+
this.rejoiner.customerId = this.customerId;
38+
if (this.metadata) {
39+
this.rejoiner.metadata = this.metadata;
40+
}
41+
42+
const response = await this.rejoiner.triggerCustomerJourney();
43+
44+
$.export(
45+
"$summary",
46+
`Triggered journey ${this.journeyId} for customer ${this.customerId}`,
47+
);
48+
return response;
49+
},
50+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import rejoiner from "../../rejoiner.app.mjs";
2+
import { axios } from "@pipedream/platform";
3+
4+
export default {
5+
key: "rejoiner-update-customer-profile",
6+
name: "Update Customer Profile",
7+
description: "Updates a customer's profile information. [See the documentation](https://docs.rejoiner.com/docs/getting-started)",
8+
version: "0.0.{{ts}}",
9+
type: "action",
10+
props: {
11+
rejoiner,
12+
customerId: {
13+
propDefinition: [
14+
rejoiner,
15+
"customerId",
16+
],
17+
},
18+
profileData: {
19+
propDefinition: [
20+
rejoiner,
21+
"profileData",
22+
],
23+
},
24+
updateSource: {
25+
propDefinition: [
26+
rejoiner,
27+
"updateSource",
28+
],
29+
optional: true,
30+
},
31+
},
32+
async run({ $ }) {
33+
const response = await this.rejoiner.updateCustomerProfile();
34+
$.export("$summary", `Updated customer profile for customer_id ${this.customerId}`);
35+
return response;
36+
},
37+
};

components/rejoiner/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
"publishConfig": {
1313
"access": "public"
1414
}
15-
}
15+
}
Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,136 @@
1+
import { axios } from "@pipedream/platform";
2+
13
export default {
24
type: "app",
35
app: "rejoiner",
4-
propDefinitions: {},
6+
propDefinitions: {
7+
customerId: {
8+
type: "string",
9+
label: "Customer ID",
10+
description: "Unique identifier for the customer",
11+
},
12+
listId: {
13+
type: "string",
14+
label: "List ID",
15+
description: "Unique identifier for the list",
16+
},
17+
listSource: {
18+
type: "string",
19+
label: "List Source",
20+
description: "Source context of the list",
21+
optional: true,
22+
},
23+
journeyId: {
24+
type: "string",
25+
label: "Journey ID",
26+
description: "Unique identifier for the journey",
27+
},
28+
metadata: {
29+
type: "string",
30+
label: "Metadata",
31+
description: "Additional data relevant to the journey start",
32+
optional: true,
33+
},
34+
profileData: {
35+
type: "object",
36+
label: "Profile Data",
37+
description: "An object containing updated profile attributes",
38+
},
39+
updateSource: {
40+
type: "string",
41+
label: "Update Source",
42+
description: "Context for the update",
43+
optional: true,
44+
},
45+
},
546
methods: {
647
// this.$auth contains connected account data
748
authKeys() {
849
console.log(Object.keys(this.$auth));
950
},
51+
_baseUrl() {
52+
return "https://api.rejoiner.com";
53+
},
54+
async _makeRequest(opts = {}) {
55+
const {
56+
$ = this,
57+
method = "GET",
58+
path = "/",
59+
headers,
60+
...otherOpts
61+
} = opts;
62+
return axios($, {
63+
...otherOpts,
64+
method,
65+
url: this._baseUrl() + path,
66+
headers: {
67+
...headers,
68+
"Authorization": `Bearer ${this.$auth.api_token}`,
69+
"Content-Type": "application/json",
70+
},
71+
});
72+
},
73+
async emitOptOutEvent() {
74+
return this._makeRequest({
75+
method: "POST",
76+
path: "/events/opt-out",
77+
data: {
78+
customer_id: this.customerId,
79+
},
80+
});
81+
},
82+
async addCustomerToList() {
83+
const data = {
84+
customer_id: this.customerId,
85+
list_id: this.listId,
86+
};
87+
if (this.listSource) {
88+
data.list_source = this.listSource;
89+
}
90+
return this._makeRequest({
91+
method: "POST",
92+
path: "/customers/lists",
93+
data,
94+
});
95+
},
96+
async triggerCustomerJourney() {
97+
const data = {
98+
journey_id: this.journeyId,
99+
customer_id: this.customerId,
100+
};
101+
if (this.metadata) {
102+
try {
103+
data.metadata = JSON.parse(this.metadata);
104+
} catch (error) {
105+
throw new Error("Invalid JSON format for metadata");
106+
}
107+
}
108+
return this._makeRequest({
109+
method: "POST",
110+
path: "/journeys/trigger",
111+
data,
112+
});
113+
},
114+
async updateCustomerProfile() {
115+
if (!this.customerId) {
116+
throw new Error("customer_id is required");
117+
}
118+
if (!this.profileData || typeof this.profileData !== "object") {
119+
throw new Error("profile_data is required and must be an object");
120+
}
121+
const data = {
122+
customer_id: this.customerId,
123+
profile_data: this.profileData,
124+
};
125+
if (this.updateSource) {
126+
data.update_source = this.updateSource;
127+
}
128+
return this._makeRequest({
129+
method: "PUT",
130+
path: `/customers/${this.customerId}/profile`,
131+
data,
132+
});
133+
},
10134
},
135+
version: "0.0.{{ts}}",
11136
};
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {
2+
axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
3+
} from "@pipedream/platform";
4+
import rejoiner from "../../rejoiner.app.mjs";
5+
6+
export default {
7+
key: "rejoiner-new-customer-opt-out-instant",
8+
name: "New Customer Opt-Out Instant",
9+
description: "Emit a new event when a customer opts out of receiving communications. [See the documentation]().",
10+
version: "0.0.{{ts}}",
11+
type: "source",
12+
dedupe: "unique",
13+
props: {
14+
rejoiner: {
15+
type: "app",
16+
app: "rejoiner",
17+
},
18+
db: "$.service.db",
19+
timer: {
20+
type: "$.interface.timer",
21+
default: {
22+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
23+
},
24+
},
25+
},
26+
methods: {
27+
_getLastTimestamp() {
28+
return this.db.get("lastTimestamp") ?? 0;
29+
},
30+
_setLastTimestamp(timestamp) {
31+
this.db.set("lastTimestamp", timestamp);
32+
},
33+
},
34+
hooks: {
35+
async deploy() {
36+
let lastTimestamp = this._getLastTimestamp();
37+
if (!lastTimestamp) {
38+
lastTimestamp = Date.now();
39+
}
40+
41+
const optOutEvents = await this.rejoiner._makeRequest({
42+
method: "GET",
43+
path: "/events/opt-out",
44+
params: {
45+
limit: 50,
46+
},
47+
});
48+
49+
optOutEvents.reverse();
50+
51+
for (const event of optOutEvents) {
52+
const ts = event.timestamp
53+
? Date.parse(event.timestamp)
54+
: Date.now();
55+
this.$emit(event, {
56+
id: event.id
57+
? event.id.toString()
58+
: undefined,
59+
summary: `Customer opted out: ${event.customer_id}`,
60+
ts,
61+
});
62+
if (ts > lastTimestamp) {
63+
lastTimestamp = ts;
64+
}
65+
}
66+
67+
this._setLastTimestamp(lastTimestamp);
68+
},
69+
async activate() {
70+
// No activation steps required
71+
},
72+
async deactivate() {
73+
// No deactivation steps required
74+
},
75+
},
76+
async run() {
77+
const lastTimestamp = this._getLastTimestamp();
78+
const optOutEvents = await this.rejoiner._makeRequest({
79+
method: "GET",
80+
path: "/events/opt-out",
81+
params: {
82+
since: lastTimestamp,
83+
},
84+
});
85+
86+
let newLastTimestamp = lastTimestamp;
87+
88+
for (const event of optOutEvents) {
89+
const ts = event.timestamp
90+
? Date.parse(event.timestamp)
91+
: Date.now();
92+
this.$emit(event, {
93+
id: event.id
94+
? event.id.toString()
95+
: undefined,
96+
summary: `Customer opted out: ${event.customer_id}`,
97+
ts,
98+
});
99+
if (ts > newLastTimestamp) {
100+
newLastTimestamp = ts;
101+
}
102+
}
103+
104+
if (newLastTimestamp > lastTimestamp) {
105+
this._setLastTimestamp(newLastTimestamp);
106+
}
107+
},
108+
};

0 commit comments

Comments
 (0)