Skip to content

Commit e820059

Browse files
committed
refactor: refactor crm apps into a base class
1 parent a9776d1 commit e820059

File tree

6 files changed

+210
-459
lines changed

6 files changed

+210
-459
lines changed

.env.appStore.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ ZOHOCRM_CLIENT_SECRET=""
128128

129129

130130
# - REVERT
131-
# Used for the Pipedrive integration (via/ Revert (https://revert.dev))
131+
# Used for the CRM integrations (via/ Revert (https://revert.dev))
132132
# @see https://github.com/calcom/cal.com/#obtaining-revert-api-keys
133133
REVERT_API_KEY=
134134
REVERT_PUBLIC_TOKEN=

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,9 @@ UNKEY_ROOT_KEY=
354354
# Used for Cal.ai Enterprise Voice AI Agents
355355
# https://retellai.com
356356
RETELL_AI_KEY=
357+
358+
# - REVERT
359+
# Used for the CRM integrations (via/ Revert (https://revert.dev))
360+
# @see https://github.com/calcom/cal.com/#obtaining-revert-api-keys
361+
REVERT_API_KEY=
362+
REVERT_PUBLIC_TOKEN=
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { getLocation } from "@calcom/lib/CalEventParser";
2+
import logger from "@calcom/lib/logger";
3+
import type {
4+
Calendar,
5+
CalendarEvent,
6+
EventBusyDate,
7+
IntegrationCalendar,
8+
NewCalendarEventType,
9+
Person,
10+
} from "@calcom/types/Calendar";
11+
import type { CredentialPayload } from "@calcom/types/Credential";
12+
13+
export type ContactCreateResult = {
14+
status: string;
15+
result: {
16+
id: string;
17+
email: string;
18+
firstName: string;
19+
lastName: string;
20+
name: string;
21+
};
22+
};
23+
24+
export type ContactSearchResult = {
25+
status: string;
26+
results: Array<{
27+
id: string;
28+
email: string;
29+
firstName: string;
30+
lastName: string;
31+
name: string;
32+
}>;
33+
};
34+
35+
export default abstract class RevertCRMAppService implements Calendar {
36+
protected log: typeof logger;
37+
protected tenantId: string;
38+
protected revertApiKey: string;
39+
protected revertApiUrl: string;
40+
protected appSlug: string;
41+
42+
constructor(credential: CredentialPayload) {
43+
this.revertApiKey = process.env.REVERT_API_KEY || "";
44+
this.revertApiUrl = process.env.REVERT_API_URL || "https://api.revert.dev/";
45+
this.tenantId = String(credential.teamId ? credential.teamId : credential.userId);
46+
this.log = logger.getSubLogger({ prefix: [`[[lib]`] });
47+
this.appSlug = "";
48+
}
49+
50+
protected createContacts = async (attendees: Person[]) => {
51+
const result = attendees.map(async (attendee) => {
52+
const headers = new Headers();
53+
headers.append("x-revert-api-token", this.revertApiKey);
54+
headers.append("x-revert-t-id", this.tenantId);
55+
headers.append("Content-Type", "application/json");
56+
57+
const [firstname, lastname] = !!attendee.name ? attendee.name.split(" ") : [attendee.email, "-"];
58+
const bodyRaw = JSON.stringify({
59+
firstName: firstname,
60+
lastName: lastname || "-",
61+
email: attendee.email,
62+
});
63+
64+
const requestOptions = {
65+
method: "POST",
66+
headers: headers,
67+
body: bodyRaw,
68+
};
69+
70+
try {
71+
const response = await fetch(`${this.revertApiUrl}crm/contacts`, requestOptions);
72+
const result = (await response.json()) as ContactCreateResult;
73+
return result;
74+
} catch (error) {
75+
return Promise.reject(error);
76+
}
77+
});
78+
return await Promise.all(result);
79+
};
80+
81+
protected getMeetingBody = (event: CalendarEvent): string => {
82+
return `<b>${event.organizer.language.translate("invitee_timezone")}:</b> ${
83+
event.attendees[0].timeZone
84+
}<br><br><b>${event.organizer.language.translate("share_additional_notes")}</b><br>${
85+
event.additionalNotes || "-"
86+
}`;
87+
};
88+
89+
protected abstract contactSearch(
90+
event: CalendarEvent
91+
): Promise<ContactSearchResult | ContactSearchResult[]>;
92+
93+
protected abstract createCRMEvent(
94+
event: CalendarEvent,
95+
contacts: CalendarEvent["attendees"]
96+
): Promise<Response>;
97+
98+
protected updateMeeting = async (uid: string, event: CalendarEvent) => {
99+
const eventPayload = {
100+
subject: event.title,
101+
startDateTime: event.startTime,
102+
endDateTime: event.endTime,
103+
description: this.getMeetingBody(event),
104+
location: getLocation(event),
105+
};
106+
const headers = new Headers();
107+
headers.append("x-revert-api-token", this.revertApiKey);
108+
headers.append("x-revert-t-id", this.tenantId);
109+
headers.append("Content-Type", "application/json");
110+
111+
const eventBody = JSON.stringify(eventPayload);
112+
const requestOptions = {
113+
method: "PATCH",
114+
headers: headers,
115+
body: eventBody,
116+
};
117+
118+
return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions);
119+
};
120+
121+
protected deleteMeeting = async (uid: string) => {
122+
const headers = new Headers();
123+
headers.append("x-revert-api-token", this.revertApiKey);
124+
headers.append("x-revert-t-id", this.tenantId);
125+
126+
const requestOptions = {
127+
method: "DELETE",
128+
headers: headers,
129+
};
130+
131+
return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions);
132+
};
133+
134+
protected async handleEventCreation(event: CalendarEvent, contacts: CalendarEvent["attendees"]) {
135+
const meetingEvent = await (await this.createCRMEvent(event, contacts)).json();
136+
if (meetingEvent && meetingEvent.status === "ok") {
137+
this.log.debug("event:creation:ok", { meetingEvent });
138+
139+
return Promise.resolve({
140+
uid: meetingEvent.result.id,
141+
id: meetingEvent.result.id,
142+
type: this.appSlug,
143+
password: "",
144+
url: "",
145+
additionalInfo: { contacts, meetingEvent },
146+
});
147+
}
148+
this.log.debug("meeting:creation:notOk", { meetingEvent, event, contacts });
149+
return Promise.reject("Something went wrong when creating a meeting");
150+
}
151+
152+
async getAvailability(
153+
_dateFrom: string,
154+
_dateTo: string,
155+
_selectedCalendars: IntegrationCalendar[]
156+
): Promise<EventBusyDate[]> {
157+
return Promise.resolve([]);
158+
}
159+
160+
async listCalendars(_event?: CalendarEvent): Promise<IntegrationCalendar[]> {
161+
return Promise.resolve([]);
162+
}
163+
164+
abstract createEvent(event: CalendarEvent): Promise<NewCalendarEventType>;
165+
166+
abstract updateEvent(uid: string, event: CalendarEvent): Promise<NewCalendarEventType>;
167+
168+
public async deleteEvent(uid: string): Promise<void> {
169+
await this.deleteMeeting(uid);
170+
}
171+
}

0 commit comments

Comments
 (0)