Skip to content

Commit b225703

Browse files
committed
new components
1 parent 594fd1a commit b225703

File tree

12 files changed

+409
-9
lines changed

12 files changed

+409
-9
lines changed

components/attio/actions/create-note/create-note.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default {
44
key: "attio-create-note",
55
name: "Create Note",
66
description: "Creates a new note for a given record. The note will be linked to the specified record. [See the documentation](https://developers.attio.com/reference/post_v2-notes)",
7-
version: "0.0.{{ts}}",
7+
version: "0.0.1",
88
type: "action",
99
props: {
1010
attio,
Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import attio from "../../attio.app.mjs";
2+
import utils from "../../common/utils.mjs";
23

34
export default {
45
key: "attio-create-update-record",
56
name: "Create or Update Record",
67
description: "Creates or updates a specific record such as a person or a deal. If the record already exists, it's updated. Otherwise, a new record is created. [See the documentation](https://developers.attio.com/reference/put_v2-objects-object-records)",
7-
version: "0.0.{{ts}}",
8+
version: "0.0.1",
89
type: "action",
910
props: {
1011
attio,
@@ -14,21 +15,70 @@ export default {
1415
"objectId",
1516
],
1617
},
17-
parentRecordId: {
18+
attributeId: {
1819
propDefinition: [
1920
attio,
20-
"recordId",
21+
"attributeId",
2122
(c) => ({
2223
objectId: c.objectId,
2324
}),
2425
],
26+
reloadProps: true,
27+
},
28+
},
29+
async additionalProps() {
30+
const props = {};
31+
if (!this.attributeId) {
32+
return props;
33+
}
34+
const attributes = await this.getRelevantAttributes();
35+
for (const attribute of attributes) {
36+
props[attribute.id.attribute_id] = {
37+
type: attribute.is_multiselect
38+
? "string[]"
39+
: "string",
40+
label: attribute.title,
41+
optional: attribute.id.attribute_id !== this.attributeId && !attribute.is_required,
42+
};
43+
}
44+
return props;
45+
},
46+
methods: {
47+
async getRelevantAttributes() {
48+
const stream = utils.paginate({
49+
fn: this.attio.listAttributes,
50+
args: {
51+
objectId: this.objectId,
52+
},
53+
});
54+
const attributes = await utils.streamIterator(stream);
55+
return attributes.filter((a) => a.is_writable || a.id.attribute_id === this.attributeId);
2556
},
2657
},
2758
async run({ $ }) {
28-
const response = await this.upsertRecord({
59+
const {
60+
attio,
61+
getRelevantAttributes,
62+
objectId,
63+
attributeId,
64+
...values
65+
} = this;
66+
67+
const attributes = await getRelevantAttributes();
68+
69+
const response = await attio.upsertRecord({
2970
$,
30-
data: {},
71+
objectId,
72+
params: {
73+
matching_attribute: attributeId,
74+
},
75+
data: {
76+
data: {
77+
values: utils.parseValues(attributes, values),
78+
},
79+
},
3180
});
81+
$.export("$summary", "Successfully created or updated record");
3282
return response;
3383
},
3484
};

components/attio/actions/delete-list-entry/delete-list-entry.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default {
44
key: "attio-delete-list-entry",
55
name: "Delete List Entry",
66
description: "Deletes an existing entry from a specific list. [See the documentation](https://developers.attio.com/reference/delete_v2-lists-list-entries-entry-id)",
7-
version: "0.0.{{ts}}",
7+
version: "0.0.1",
88
type: "action",
99
props: {
1010
attio,

components/attio/attio.app.mjs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,30 @@ export default {
6767
return data?.map(({ id }) => id.record_id) || [];
6868
},
6969
},
70+
attributeId: {
71+
type: "string",
72+
label: "Attribute ID",
73+
description: "The ID or slug of the attribute to use to check if a record already exists. The attribute must be unique.",
74+
async options({
75+
objectId, page,
76+
}) {
77+
const { data } = await this.listAttributes({
78+
objectId,
79+
params: {
80+
limit: DEFAULT_LIMIT,
81+
offset: page * DEFAULT_LIMIT,
82+
},
83+
});
84+
return data
85+
?.filter((attribute) => attribute.is_unique)
86+
?.map(({
87+
id, title: label,
88+
}) => ({
89+
value: id.attribute_id,
90+
label,
91+
})) || [];
92+
},
93+
},
7094
},
7195
methods: {
7296
_baseUrl() {
@@ -85,6 +109,22 @@ export default {
85109
...opts,
86110
});
87111
},
112+
createWebhook(opts = {}) {
113+
return this._makeRequest({
114+
method: "POST",
115+
path: "/webhooks",
116+
...opts,
117+
});
118+
},
119+
deleteWebhook({
120+
hookId, ...opts
121+
}) {
122+
return this._makeRequest({
123+
method: "DELETE",
124+
path: `/webhooks/${hookId}`,
125+
...opts,
126+
});
127+
},
88128
listLists(opts = {}) {
89129
return this._makeRequest({
90130
path: "/lists",
@@ -115,6 +155,14 @@ export default {
115155
...opts,
116156
});
117157
},
158+
listAttributes({
159+
objectId, ...opts
160+
}) {
161+
return this._makeRequest({
162+
path: `/objects/${objectId}/attributes`,
163+
...opts,
164+
});
165+
},
118166
createNote(opts = {}) {
119167
return this._makeRequest({
120168
method: "POST",
@@ -123,11 +171,11 @@ export default {
123171
});
124172
},
125173
upsertRecord({
126-
objectType, ...opts
174+
objectId, ...opts
127175
}) {
128176
return this._makeRequest({
129177
method: "PUT",
130-
path: `/objects/${objectType}/records`,
178+
path: `/objects/${objectId}/records`,
131179
...opts,
132180
});
133181
},

components/attio/common/utils.mjs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
async function streamIterator(stream) {
2+
let resources = [];
3+
for await (const resource of stream) {
4+
resources.push(resource);
5+
}
6+
return resources;
7+
}
8+
9+
async function *paginate({
10+
fn,
11+
args,
12+
max,
13+
}) {
14+
args = {
15+
...args,
16+
params: {
17+
...args.params,
18+
limit: 500,
19+
offset: 0,
20+
},
21+
};
22+
let total, count = 0;
23+
do {
24+
const { data } = await fn(args);
25+
for (const item of data) {
26+
yield item;
27+
if (max && ++count >= max) {
28+
return;
29+
}
30+
}
31+
total = data?.length;
32+
args.params.offset += args.params.limit;
33+
} while (total === args.params.limit);
34+
}
35+
36+
function parseValues(attributes, values) { console.log(attributes);
37+
for (const [
38+
key,
39+
value,
40+
] of Object.entries(values)) {
41+
const {
42+
type, is_multiselect: isMultiselect,
43+
} = attributes.find(({ id }) => id.attribute_id === key);
44+
if (type === "checkbox") {
45+
values[key] = isMultiselect
46+
? value.map((v) => !(v === "false" || v === "0"))
47+
: !(value === "false" || value === "0");
48+
}
49+
if (type === "number" || type === "rating") {
50+
values[key] = isMultiselect
51+
? value.map((v) => +v)
52+
: +value;
53+
}
54+
}
55+
return values;
56+
}
57+
58+
export default {
59+
streamIterator,
60+
paginate,
61+
parseValues,
62+
};
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import attio from "../../attio.app.mjs";
2+
3+
export default {
4+
props: {
5+
attio,
6+
db: "$.service.db",
7+
http: "$.interface.http",
8+
},
9+
hooks: {
10+
async activate() {
11+
const { data: { id: { webhook_id: hookId } } } = await this.attio.createWebhook({
12+
data: {
13+
data: {
14+
target_url: this.http.endpoint,
15+
subscriptions: [
16+
{
17+
event_type: this.getEventType(),
18+
filter: this.getFilter(),
19+
},
20+
],
21+
},
22+
},
23+
});
24+
this._setHookId(hookId);
25+
},
26+
async deactivate() {
27+
const hookId = this._getHookId();
28+
if (hookId) {
29+
await this.attio.deleteWebhook({
30+
hookId,
31+
});
32+
}
33+
},
34+
},
35+
methods: {
36+
_getHookId() {
37+
return this.db.get("hookId");
38+
},
39+
_setHookId(hookId) {
40+
this.db.set("hookId", hookId);
41+
},
42+
getFilter() {
43+
return null;
44+
},
45+
getEventType() {
46+
throw new Error("getEventType is not implemented");
47+
},
48+
generateMeta() {
49+
throw new Error("generateMeta is not implemented");
50+
},
51+
},
52+
async run(event) {
53+
const { body: { events } } = event;
54+
if (!events?.length) {
55+
return;
56+
}
57+
events.forEach((item) => {
58+
const meta = this.generateMeta(item);
59+
this.$emit(item, meta);
60+
});
61+
},
62+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import common from "../common/base.mjs";
2+
import sampleEmit from "./test-event";
3+
4+
export default {
5+
...common,
6+
key: "attio-new-list-entry-instant",
7+
name: "New List Entry (Instant)",
8+
description: "Emit new event when a record, such as person, company, or deal, is added to a list",
9+
version: "0.0.1",
10+
type: "source",
11+
dedupe: "unique",
12+
props: {
13+
...common.props,
14+
listId: {
15+
propDefinition: [
16+
common.props.attio,
17+
"listId",
18+
],
19+
},
20+
},
21+
methods: {
22+
...common.methods,
23+
getEventType() {
24+
return "list-entry.created";
25+
},
26+
getFilter() {
27+
return {
28+
"$and": [
29+
{
30+
field: "id.list_id",
31+
operator: "equals",
32+
value: this.listId,
33+
},
34+
],
35+
};
36+
},
37+
generateMeta(entry) {
38+
return {
39+
id: entry.id.entry_id,
40+
summary: `New Entry with ID: ${entry.id.entry_id}`,
41+
ts: Date.now(),
42+
};
43+
},
44+
},
45+
sampleEmit,
46+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
"event_type": "list-entry.created",
3+
"id": {
4+
"workspace_id": "14beef7a-99f7-4534-a87e-70b564330a4c",
5+
"list_id": "33ebdbe9-e529-47c9-b894-0ba25e9c15c0",
6+
"entry_id": "2e6e29ea-c4e0-4f44-842d-78a891f8c156"
7+
},
8+
"parent_object_id": "97052eb9-e65e-443f-a297-f2d9a4a7f795",
9+
"parent_record_id": "bf071e1f-6035-429d-b874-d83ea64ea13b",
10+
"actor": {
11+
"type": "workspace-member",
12+
"id": "50cf242c-7fa3-4cad-87d0-75b1af71c57b"
13+
}
14+
}

0 commit comments

Comments
 (0)