Skip to content

Commit 4e1168f

Browse files
committed
[ACTION] Hubspot Meetings
1 parent f15cf0d commit 4e1168f

File tree

6 files changed

+480
-9
lines changed

6 files changed

+480
-9
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/* eslint-disable no-unused-vars */
2+
import common from "../common/common-create.mjs";
3+
import { ConfigurationError } from "@pipedream/platform";
4+
import {
5+
ASSOCIATION_CATEGORY, OBJECT_TYPE,
6+
} from "../../common/constants.mjs";
7+
8+
export default {
9+
...common,
10+
key: "hubspot-create-meeting",
11+
name: "Create Meeting",
12+
description: "Creates a new meeting with optional associations to other objects. [See the documentation](https://developers.hubspot.com/docs/reference/api/crm/engagements/meetings#post-%2Fcrm%2Fv3%2Fobjects%2Fmeetings)",
13+
version: "0.0.1",
14+
type: "action",
15+
props: {
16+
...common.props,
17+
toObjectType: {
18+
propDefinition: [
19+
common.props.hubspot,
20+
"objectType",
21+
],
22+
label: "Associated Object Type",
23+
description: "Type of object the meeting is being associated with",
24+
},
25+
toObjectId: {
26+
propDefinition: [
27+
common.props.hubspot,
28+
"objectId",
29+
(c) => ({
30+
objectType: c.toObjectType,
31+
}),
32+
],
33+
label: "Associated Object",
34+
description: "ID of object the meeting is being associated with",
35+
optional: true,
36+
},
37+
associationType: {
38+
propDefinition: [
39+
common.props.hubspot,
40+
"associationType",
41+
(c) => ({
42+
fromObjectType: "meetings",
43+
toObjectType: c.toObjectType,
44+
}),
45+
],
46+
description: "A unique identifier to indicate the association type between the meeting and the other object",
47+
optional: true,
48+
},
49+
objectProperties: {
50+
type: "object",
51+
label: "Meeting Properties",
52+
description: "Enter the meeting properties as a JSON object. Required properties: hs_meeting_title, hs_meeting_body, hs_meeting_start_time, hs_meeting_end_time. Optional: hs_meeting_status",
53+
},
54+
},
55+
methods: {
56+
...common.methods,
57+
getObjectType() {
58+
return OBJECT_TYPE.MEETING;
59+
},
60+
isRelevantProperty(property) {
61+
return common.methods.isRelevantProperty(property);
62+
},
63+
createMeeting(properties, associations, $) {
64+
return this.hubspot.createMeeting({
65+
data: {
66+
properties,
67+
associations,
68+
},
69+
$,
70+
});
71+
},
72+
},
73+
async run({ $ }) {
74+
const {
75+
hubspot,
76+
toObjectType,
77+
toObjectId,
78+
associationType,
79+
objectProperties,
80+
...otherProperties
81+
} = this;
82+
83+
if ((toObjectId && !associationType) || (!toObjectId && associationType)) {
84+
throw new ConfigurationError("Both `toObjectId` and `associationType` must be entered");
85+
}
86+
87+
const properties = objectProperties
88+
? typeof objectProperties === "string"
89+
? JSON.parse(objectProperties)
90+
: objectProperties
91+
: otherProperties;
92+
93+
const associations = toObjectId
94+
? [
95+
{
96+
to: {
97+
id: toObjectId,
98+
},
99+
types: [
100+
{
101+
associationTypeId: associationType,
102+
associationCategory: ASSOCIATION_CATEGORY.HUBSPOT_DEFINED,
103+
},
104+
],
105+
},
106+
]
107+
: undefined;
108+
109+
const meeting = await this.createMeeting(properties, associations, $);
110+
111+
$.export("$summary", `Successfully created meeting "${properties.hs_meeting_title}"`);
112+
113+
return meeting;
114+
},
115+
};
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import { OBJECT_TYPE } from "../../common/object-types.mjs";
2+
import hubspot from "../../hubspot.app.mjs";
3+
4+
export default {
5+
key: "hubspot-get-associated-meetings",
6+
name: "Get Associated Meetings",
7+
description: "Retrieves meetings associated with a specific object (contact, company, or deal) with optional time filtering. [See the documentation](https://developers.hubspot.com/docs/reference/api/crm/associations/association-details#get-%2Fcrm%2Fv4%2Fobjects%2F%7Bobjecttype%7D%2F%7Bobjectid%7D%2Fassociations%2F%7Btoobjecttype%7D)",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
hubspot,
12+
objectType: {
13+
propDefinition: [
14+
hubspot,
15+
"objectType",
16+
() => ({
17+
includeCustom: true,
18+
}),
19+
],
20+
label: "From Object Type",
21+
description: "The type of the object being associated",
22+
},
23+
objectId: {
24+
type: "string",
25+
label: "Object ID",
26+
description: "The ID of the object to get associated meetings for. For contacts, you can search by email.",
27+
propDefinition: [
28+
hubspot,
29+
"objectIds",
30+
({ objectType }) => ({
31+
objectType,
32+
}),
33+
],
34+
},
35+
timeframe: {
36+
propDefinition: [
37+
hubspot,
38+
"timeframe",
39+
],
40+
},
41+
startDate: {
42+
propDefinition: [
43+
hubspot,
44+
"startDate",
45+
],
46+
},
47+
endDate: {
48+
propDefinition: [
49+
hubspot,
50+
"endDate",
51+
],
52+
},
53+
mostRecent: {
54+
propDefinition: [
55+
hubspot,
56+
"mostRecent",
57+
],
58+
},
59+
},
60+
methods: {
61+
/**
62+
* Get time frame filter for meetings based on selected option
63+
* @param {string} timeframe - The selected timeframe option
64+
* @param {string} startDate - Custom start date for custom range
65+
* @param {string} endDate - Custom end date for custom range
66+
* @returns {object} Filter object for the search query
67+
*/
68+
getMeetingTimeFilter(timeframe, startDate, endDate) {
69+
const now = new Date();
70+
const startOfDay = new Date(now.setHours(0, 0, 0, 0)).toISOString();
71+
const endOfDay = new Date(now.setHours(23, 59, 59, 999)).toISOString();
72+
73+
switch (timeframe) {
74+
case "today":
75+
return {
76+
hs_meeting_start_time: {
77+
$gte: startOfDay,
78+
$lte: endOfDay,
79+
},
80+
};
81+
case "this_week": {
82+
const startOfWeek = new Date(now.setDate(now.getDate() - now.getDay())).toISOString();
83+
const endOfWeek = new Date(now.setDate(now.getDate() - now.getDay() + 6)).toISOString();
84+
return {
85+
hs_meeting_start_time: {
86+
$gte: startOfWeek,
87+
$lte: endOfWeek,
88+
},
89+
};
90+
}
91+
case "this_month": {
92+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
93+
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString();
94+
return {
95+
hs_meeting_start_time: {
96+
$gte: startOfMonth,
97+
$lte: endOfMonth,
98+
},
99+
};
100+
}
101+
case "last_month": {
102+
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString();
103+
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0).toISOString();
104+
return {
105+
hs_meeting_start_time: {
106+
$gte: startOfLastMonth,
107+
$lte: endOfLastMonth,
108+
},
109+
};
110+
}
111+
case "custom":
112+
if (!startDate || !endDate) return {};
113+
return {
114+
hs_meeting_start_time: {
115+
$gte: startDate,
116+
$lte: endDate,
117+
},
118+
};
119+
default:
120+
return {};
121+
}
122+
},
123+
async getAssociatedMeetings({
124+
objectType,
125+
objectId,
126+
timeframe,
127+
startDate,
128+
endDate,
129+
mostRecent,
130+
}) {
131+
const { results: associations } = await this.hubspot.getAssociations({
132+
objectType,
133+
objectId,
134+
toObjectType: OBJECT_TYPE.MEETING,
135+
});
136+
137+
if (!associations?.length) {
138+
return [];
139+
}
140+
141+
const meetingIds = associations.map((assoc) => assoc.toObjectId);
142+
143+
const timeFilter = this.getMeetingTimeFilter(timeframe, startDate, endDate);
144+
const filter = {
145+
...timeFilter,
146+
id: {
147+
values: meetingIds,
148+
},
149+
};
150+
151+
const { results } = await this.hubspot.searchMeetings({
152+
data: {
153+
filterGroups: [
154+
{
155+
filters: Object.entries(filter).map(([
156+
property,
157+
value,
158+
]) => ({
159+
propertyName: property,
160+
operator: "IN",
161+
...value,
162+
})),
163+
},
164+
],
165+
sorts: [
166+
{
167+
propertyName: "hs_meeting_start_time",
168+
direction: "DESCENDING",
169+
},
170+
],
171+
limit: mostRecent
172+
? 1
173+
: 100,
174+
},
175+
});
176+
177+
return results;
178+
},
179+
},
180+
async run({ $ }) {
181+
let resolvedObjectId = this.objectId;
182+
if (this.objectType === OBJECT_TYPE.CONTACT && this.objectId.includes("@")) {
183+
const { results } = await this.hubspot.searchCRM({
184+
object: OBJECT_TYPE.CONTACT,
185+
data: {
186+
filterGroups: [
187+
{
188+
filters: [
189+
{
190+
propertyName: "email",
191+
operator: "EQ",
192+
value: this.objectId,
193+
},
194+
],
195+
},
196+
],
197+
},
198+
});
199+
if (!results?.length) {
200+
throw new Error(`No contact found with email: ${this.objectId}`);
201+
}
202+
resolvedObjectId = results[0].id;
203+
}
204+
205+
const meetings = await this.getAssociatedMeetings({
206+
objectType: this.objectType,
207+
objectId: resolvedObjectId,
208+
timeframe: this.timeframe,
209+
startDate: this.startDate,
210+
endDate: this.endDate,
211+
mostRecent: this.mostRecent,
212+
});
213+
214+
const summary = this.mostRecent
215+
? "Successfully retrieved most recent meeting"
216+
: `Successfully retrieved ${meetings.length} meeting(s)`;
217+
218+
$.export("$summary", summary);
219+
220+
return meetings;
221+
},
222+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import hubspot from "../../hubspot.app.mjs";
2+
3+
export default {
4+
key: "hubspot-get-meeting",
5+
name: "Get Meeting",
6+
description: "Retrieves a specific meeting by its ID. [See the documentation](https://developers.hubspot.com/docs/reference/api/crm/engagements/meetings#get-%2Fcrm%2Fv3%2Fobjects%2Fmeetings%2F%7Bmeetingid%7D)",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
hubspot,
11+
meetingId: {
12+
propDefinition: [
13+
hubspot,
14+
"meetingId",
15+
],
16+
},
17+
},
18+
async run({ $ }) {
19+
const response = await this.hubspot.getMeeting({
20+
meetingId: this.meetingId,
21+
$,
22+
});
23+
24+
$.export("$summary", `Successfully retrieved meeting with ID \`${response.id}\`.`);
25+
26+
return response;
27+
},
28+
};

0 commit comments

Comments
 (0)