Skip to content

Commit 4b94a90

Browse files
New Components - refiner (#15497)
* refiner init * [Components] tinyurl #15135 Sources - New Survey Completion - New Segment Entry Actions - Identify User - Track Event * pnpm update * Update components/refiner/sources/new-segment-entry/new-segment-entry.mjs Co-authored-by: michelle0927 <[email protected]> * some adjusts * fix import * some adjusts --------- Co-authored-by: michelle0927 <[email protected]>
1 parent 33a14e3 commit 4b94a90

File tree

10 files changed

+425
-7
lines changed

10 files changed

+425
-7
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ConfigurationError } from "@pipedream/platform";
2+
import refiner from "../../refiner.app.mjs";
3+
4+
export default {
5+
key: "refiner-identify-user",
6+
name: "Identify User",
7+
description: "Identify a user with user ID or email. If the user does not exist, a new one will be created. [See the documentation](https://refiner.io/docs/api/#identify-user)",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
refiner,
12+
userId: {
13+
propDefinition: [
14+
refiner,
15+
"userId",
16+
],
17+
optional: true,
18+
},
19+
email: {
20+
propDefinition: [
21+
refiner,
22+
"email",
23+
],
24+
optional: true,
25+
},
26+
},
27+
async run({ $ }) {
28+
if (!this.userId && !this.email) {
29+
throw new ConfigurationError("Either User ID or Email must be provided to identify the user.");
30+
}
31+
32+
const response = await this.refiner.identifyUser({
33+
$,
34+
data: {
35+
id: this.userId,
36+
email: this.email,
37+
},
38+
});
39+
40+
$.export("$summary", `User identified successfully. Contact UUID: ${response.contact_uuid}`);
41+
return response;
42+
},
43+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { ConfigurationError } from "@pipedream/platform";
2+
import refiner from "../../refiner.app.mjs";
3+
4+
export default {
5+
key: "refiner-track-event",
6+
name: "Track Event",
7+
description: "Tracks a user event in Refiner. [See the documentation](https://refiner.io/docs/api/#track-event)",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
refiner,
12+
eventName: {
13+
type: "string",
14+
label: "Event Name",
15+
description: "The name of the event or signal being tracked.",
16+
},
17+
userId: {
18+
propDefinition: [
19+
refiner,
20+
"userId",
21+
],
22+
optional: true,
23+
},
24+
email: {
25+
propDefinition: [
26+
refiner,
27+
"email",
28+
],
29+
optional: true,
30+
},
31+
},
32+
async run({ $ }) {
33+
if (!this.userId && !this.email) {
34+
throw new ConfigurationError("Either User ID or Email must be provided to track the event.");
35+
}
36+
37+
const response = await this.refiner.trackEvent({
38+
$,
39+
data: {
40+
event: this.eventName,
41+
id: this.userId,
42+
email: this.email,
43+
},
44+
});
45+
46+
$.export("$summary", `Tracked event "${this.eventName}" successfully.`);
47+
return response;
48+
},
49+
};

components/refiner/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/refiner",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Pipedream Refiner Components",
55
"main": "refiner.app.mjs",
66
"keywords": [
@@ -11,5 +11,8 @@
1111
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "^3.0.3"
1417
}
15-
}
18+
}

components/refiner/refiner.app.mjs

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,133 @@
1+
import { axios } from "@pipedream/platform";
2+
13
export default {
24
type: "app",
35
app: "refiner",
4-
propDefinitions: {},
6+
propDefinitions: {
7+
userId: {
8+
type: "string",
9+
label: "User ID",
10+
description: "The ID of the user to identify or track events for.",
11+
async options({ page }) {
12+
const { items } = await this.listContacts({
13+
params: {
14+
page: page + 1,
15+
},
16+
});
17+
18+
return items.map(({
19+
uuid: value, email: label,
20+
}) => ({
21+
label,
22+
value,
23+
}));
24+
},
25+
},
26+
email: {
27+
type: "string",
28+
label: "Email",
29+
description: "The email address of the user to identify or track events for.",
30+
optional: true,
31+
},
32+
segmentId: {
33+
type: "string",
34+
label: "Segment ID",
35+
description: "The ID of the segment to emit events for.",
36+
async options({ page }) {
37+
const { items } = await this.listSegments({
38+
params: {
39+
page: page + 1,
40+
},
41+
});
42+
43+
return items.map(({
44+
uuid: value, name: label,
45+
}) => ({
46+
label,
47+
value,
48+
}));
49+
},
50+
},
51+
},
552
methods: {
6-
// this.$auth contains connected account data
7-
authKeys() {
8-
console.log(Object.keys(this.$auth));
53+
_baseUrl() {
54+
return "https://api.refiner.io/v1";
55+
},
56+
_headers() {
57+
return {
58+
"Authorization": `Bearer ${this.$auth.api_key}`,
59+
};
60+
},
61+
_makeRequest({
62+
$ = this, path, ...opts
63+
}) {
64+
return axios($, {
65+
url: this._baseUrl() + path,
66+
headers: this._headers(),
67+
...opts,
68+
});
69+
},
70+
listContacts(opts = {}) {
71+
return this._makeRequest({
72+
path: "/contacts",
73+
...opts,
74+
});
75+
},
76+
listResponses(opts = {}) {
77+
return this._makeRequest({
78+
path: "/responses",
79+
...opts,
80+
});
81+
},
82+
listSegments(opts = {}) {
83+
return this._makeRequest({
84+
path: "/segments",
85+
...opts,
86+
});
87+
},
88+
identifyUser(opts = {}) {
89+
return this._makeRequest({
90+
method: "POST",
91+
path: "/identify-user",
92+
...opts,
93+
});
94+
},
95+
trackEvent(opts = {}) {
96+
return this._makeRequest({
97+
method: "POST",
98+
path: "/track-event",
99+
...opts,
100+
});
101+
},
102+
async *paginate({
103+
fn, params = {}, maxResults = null, ...opts
104+
}) {
105+
let hasMore = false;
106+
let count = 0;
107+
let page = 0;
108+
109+
do {
110+
params.page = ++page;
111+
const {
112+
items,
113+
pagination: {
114+
current_page, last_page,
115+
},
116+
} = await fn({
117+
params,
118+
...opts,
119+
});
120+
for (const d of items) {
121+
yield d;
122+
123+
if (maxResults && ++count === maxResults) {
124+
return count;
125+
}
126+
}
127+
128+
hasMore = current_page < last_page;
129+
130+
} while (hasMore);
9131
},
10132
},
11133
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
2+
import refiner from "../../refiner.app.mjs";
3+
4+
export default {
5+
props: {
6+
refiner,
7+
db: "$.service.db",
8+
timer: {
9+
type: "$.interface.timer",
10+
default: {
11+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
12+
},
13+
},
14+
},
15+
methods: {
16+
_getLastDate() {
17+
return this.db.get("lastDate") || 0;
18+
},
19+
_setLastDate(lastDate) {
20+
this.db.set("lastDate", lastDate);
21+
},
22+
async emitEvent(maxResults = false) {
23+
const lastDate = this._getLastDate();
24+
25+
const response = this.refiner.paginate({
26+
fn: this.getFunction(),
27+
params: this.getParams(),
28+
});
29+
30+
let responseArray = [];
31+
for await (const item of response) {
32+
const itemDate = this.getItemDate(item);
33+
if (Date.parse(itemDate) <= lastDate) break;
34+
responseArray.push(item);
35+
}
36+
37+
if (responseArray.length) {
38+
if (maxResults && (responseArray.length > maxResults)) {
39+
responseArray.length = maxResults;
40+
}
41+
const itemDate = this.getItemDate(responseArray[0]);
42+
this._setLastDate(itemDate);
43+
}
44+
45+
for (const item of responseArray.reverse()) {
46+
const itemDate = this.getItemDate(item);
47+
48+
this.$emit(item, {
49+
id: item.uuid,
50+
summary: this.getSummary(item),
51+
ts: Date.parse(itemDate),
52+
});
53+
}
54+
},
55+
},
56+
hooks: {
57+
async deploy() {
58+
await this.emitEvent(25);
59+
},
60+
},
61+
async run() {
62+
await this.emitEvent();
63+
},
64+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import common from "../common/base.mjs";
2+
import sampleEmit from "./test-event.mjs";
3+
4+
export default {
5+
...common,
6+
key: "refiner-new-segment-entry",
7+
name: "New Segment Entry",
8+
description: "Emit new event whenever a user enters a segment in Refiner. [See the documentation](https://refiner.io/docs/api/#get-contacts)",
9+
version: "0.0.1",
10+
type: "source",
11+
dedupe: "unique",
12+
props: {
13+
...common.props,
14+
segmentId: {
15+
propDefinition: [
16+
common.props.refiner,
17+
"segmentId",
18+
],
19+
},
20+
},
21+
methods: {
22+
...common.methods,
23+
getFunction() {
24+
return this.refiner.listContacts;
25+
},
26+
getParams() {
27+
return {
28+
segment_uuid: this.segmentId,
29+
};
30+
},
31+
getSummary(item) {
32+
return `User ${item.email} entered segment ${this.segmentId}`;
33+
},
34+
getItemDate(item) {
35+
return item.segments
36+
.filter(({ uuid }) => uuid === this.segmentId)[0].created_at;
37+
},
38+
},
39+
sampleEmit,
40+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export default {
2+
"uuid": "15cce3d0-ed5d-11ea-aaef-e58a2a43e996",
3+
"remote_id": "Your-Contact-ID",
4+
"email": "[email protected]",
5+
"display_name": "Jane Doe",
6+
"first_seen_at": "2020-09-02T20:44:24.000000Z",
7+
"last_seen_at": "2020-09-02T21:57:50.000000Z",
8+
"attributes": {
9+
"a_user_attribute": "Manager",
10+
"another_one": "Marketing",
11+
"a_survey_answer": "9",
12+
"another_answer": "ABC",
13+
},
14+
"segments": [
15+
{
16+
"uuid": "0ff87720-9ae5-11ea-bce5-65a395204572",
17+
"name": "Power Users Segment"
18+
},
19+
],
20+
"account": {
21+
"uuid": "15d08cc0-ed5d-11ea-b2ce-c1b46bd4b7c4",
22+
"remote_id": "Your-Account-Id",
23+
"domain": "awesome.com",
24+
"display_name": "Awesome Inc.",
25+
"first_seen_at": "2020-09-02T20:44:24.000000Z",
26+
"last_seen_at": "2020-09-02T21:57:50.000000Z",
27+
"attributes": {
28+
"an_account_attribute": "Computer Software",
29+
"another_one": "2020",
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)