Skip to content

Commit

Permalink
refactor: refactor crm apps into a base class
Browse files Browse the repository at this point in the history
  • Loading branch information
jatinsandilya committed Apr 8, 2024
1 parent b4c0c98 commit f1f5336
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 459 deletions.
2 changes: 1 addition & 1 deletion .env.appStore.example
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ ZOHOCRM_CLIENT_SECRET=""


# - REVERT
# Used for the Pipedrive integration (via/ Revert (https://revert.dev))
# Used for the CRM integrations (via/ Revert (https://revert.dev))
# @see https://github.com/calcom/cal.com/#obtaining-revert-api-keys
REVERT_API_KEY=
REVERT_PUBLIC_TOKEN=
Expand Down
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,9 @@ UNKEY_ROOT_KEY=
# Used for Cal.ai Enterprise Voice AI Agents
# https://retellai.com
RETELL_AI_KEY=

# - REVERT
# Used for the CRM integrations (via/ Revert (https://revert.dev))
# @see https://github.com/calcom/cal.com/#obtaining-revert-api-keys
REVERT_API_KEY=
REVERT_PUBLIC_TOKEN=
171 changes: 171 additions & 0 deletions packages/app-store/_utils/crms/RevertCRMAppService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { getLocation } from "@calcom/lib/CalEventParser";
import logger from "@calcom/lib/logger";
import type {
Calendar,
CalendarEvent,
EventBusyDate,
IntegrationCalendar,
NewCalendarEventType,
Person,
} from "@calcom/types/Calendar";
import type { CredentialPayload } from "@calcom/types/Credential";

export type ContactCreateResult = {
status: string;
result: {
id: string;
email: string;
firstName: string;
lastName: string;
name: string;
};
};

export type ContactSearchResult = {
status: string;
results: Array<{
id: string;
email: string;
firstName: string;
lastName: string;
name: string;
}>;
};

export default abstract class RevertCRMAppService implements Calendar {
protected log: typeof logger;
protected tenantId: string;
protected revertApiKey: string;
protected revertApiUrl: string;
protected appSlug: string;

constructor(credential: CredentialPayload) {
this.revertApiKey = process.env.REVERT_API_KEY || "";
this.revertApiUrl = process.env.REVERT_API_URL || "https://api.revert.dev/";
this.tenantId = String(credential.teamId ? credential.teamId : credential.userId);
this.log = logger.getSubLogger({ prefix: [`[[lib]`] });
this.appSlug = "";
}

protected createContacts = async (attendees: Person[]) => {
const result = attendees.map(async (attendee) => {
const headers = new Headers();
headers.append("x-revert-api-token", this.revertApiKey);
headers.append("x-revert-t-id", this.tenantId);
headers.append("Content-Type", "application/json");

const [firstname, lastname] = !!attendee.name ? attendee.name.split(" ") : [attendee.email, "-"];
const bodyRaw = JSON.stringify({
firstName: firstname,
lastName: lastname || "-",
email: attendee.email,
});

const requestOptions = {
method: "POST",
headers: headers,
body: bodyRaw,
};

try {
const response = await fetch(`${this.revertApiUrl}crm/contacts`, requestOptions);
const result = (await response.json()) as ContactCreateResult;
return result;
} catch (error) {
return Promise.reject(error);
}
});
return await Promise.all(result);
};

protected getMeetingBody = (event: CalendarEvent): string => {
return `<b>${event.organizer.language.translate("invitee_timezone")}:</b> ${
event.attendees[0].timeZone
}<br><br><b>${event.organizer.language.translate("share_additional_notes")}</b><br>${
event.additionalNotes || "-"
}`;
};

protected abstract contactSearch(
event: CalendarEvent
): Promise<ContactSearchResult | ContactSearchResult[]>;

protected abstract createCRMEvent(
event: CalendarEvent,
contacts: CalendarEvent["attendees"]
): Promise<Response>;

protected updateMeeting = async (uid: string, event: CalendarEvent) => {
const eventPayload = {
subject: event.title,
startDateTime: event.startTime,
endDateTime: event.endTime,
description: this.getMeetingBody(event),
location: getLocation(event),
};
const headers = new Headers();
headers.append("x-revert-api-token", this.revertApiKey);
headers.append("x-revert-t-id", this.tenantId);
headers.append("Content-Type", "application/json");

const eventBody = JSON.stringify(eventPayload);
const requestOptions = {
method: "PATCH",
headers: headers,
body: eventBody,
};

return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions);
};

protected deleteMeeting = async (uid: string) => {
const headers = new Headers();
headers.append("x-revert-api-token", this.revertApiKey);
headers.append("x-revert-t-id", this.tenantId);

const requestOptions = {
method: "DELETE",
headers: headers,
};

return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions);
};

protected async handleEventCreation(event: CalendarEvent, contacts: CalendarEvent["attendees"]) {
const meetingEvent = await (await this.createCRMEvent(event, contacts)).json();
if (meetingEvent && meetingEvent.status === "ok") {
this.log.debug("event:creation:ok", { meetingEvent });

return Promise.resolve({
uid: meetingEvent.result.id,
id: meetingEvent.result.id,
type: this.appSlug,
password: "",
url: "",
additionalInfo: { contacts, meetingEvent },
});
}
this.log.debug("meeting:creation:notOk", { meetingEvent, event, contacts });
return Promise.reject("Something went wrong when creating a meeting");
}

async getAvailability(
_dateFrom: string,
_dateTo: string,
_selectedCalendars: IntegrationCalendar[]
): Promise<EventBusyDate[]> {
return Promise.resolve([]);
}

async listCalendars(_event?: CalendarEvent): Promise<IntegrationCalendar[]> {
return Promise.resolve([]);
}

abstract createEvent(event: CalendarEvent): Promise<NewCalendarEventType>;

abstract updateEvent(uid: string, event: CalendarEvent): Promise<NewCalendarEventType>;

public async deleteEvent(uid: string): Promise<void> {
await this.deleteMeeting(uid);
}
}
Loading

0 comments on commit f1f5336

Please sign in to comment.