Files
cal.diy/packages/features/tasker/tasks/crm/createCRMEvent.ts
T
+8 6561b92f40 perf: Move CRM event creation to tasker (#18370)
* Upgrade jsforce to 3.6.2

* Refactor connecting to Salesforce

* Revert yarn.lock changes

* Add `TASKER_ENABLE_CRM_EVENT_CREATION` to .env

* Add createCRMEvent scheduler

* Schedule CRM event creating in `EventManager`

* Add calendar event builder for CRM tasks

* Do not write to person record if fields don't exist

* Change type to expect string from tasker

* Create CRM event

* Create booking references

* Type fixes

* Migrate callback endpoint

* Add jsforce node dependency

* Migrate add endpoint

* Import

* Import package into crmService

* Use new package types

* Type fix

* Update vite config

* Push updated lockfile

* Attempt to bump platform/libraries to unlock jsforce

* Also update lockfile, naturally

* bump platform libraries

* feat: salesforce to tasker improvements (#18419)

* feat: salesforce to tasker

* refactor: event manager

* tests: add unit tests for create CRM Event

* Update vite.config

* Add jsforce to vite config

* Revert mint.json changes

* Default to not enabling

* Revert yarn.lock changes

* Remove `TASKER_ENABLE_CRM_EVENT_CREATION` variable

* Revert yarn.lock changes

* feat: Round Robin weights future members toggle (#17782)

Co-authored-by: Omar López <zomars@me.com>

* detailed customer card (#18511)

Co-authored-by: Omar López <zomars@me.com>

* chore: app router - all sub-pages in `/apps` (#16976)

* chore: apps/[slug] remove pages router

* remove apps/[slug] pages from /future

* chore: apps/installed remove pages router

* chore: apps/installation remove pages router

* remove Head element

* fix metadata

* fix test

* fix another test

* chore: apps/categories remove pages router

* revert unneeded changes

* update middleware

* Remove <Head>

* remove unused import and code

* remove unused import and code again

* fix

* fix category page

* add split icon

* add /routing paths to middleware matcher

* wip

* remove HeadSeo from App.tsx

* clean up head-seo test

* add generateAppMetadata

* use generateAppMetadata in apps/[slug] page

* delete file

* remove log

* fix

* fix

* fix apps/installed pages

* fix cateogires pages

* fix

* fix imports

* wip

* fix

* fix

* fix metadata

* fix

* redirect /apps/routing-forms to /routing

* replace all usages of /apps/routing-forms to /routing

* better naming

* /routing -> /routing/forms

* fix

* fix

* fix

* fix

* remove backPath as it is irrelevant when withoutMain is true

* fix type checks

* fix type check in apps/[slug]

* refactors

* fix

* fix test

* fix

* fix

* fix

* Replace multiple leading slashes with a single slash

* migrate routing-forms too

* add re routing

* fix

* add redirection

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>

* chore: app router 404 page (#18597)

* wip

* wip

* fix not found page

* render middleware for /settings pages

* fix

* remove global-error page

* add metadata to not-found page

* make not-found page static

* remove 404

* adding not-found to middleware is not necessary

* add every routes to config.matcher

* fix test

* fix style

* use i18n string

* fix tests

* fix

* fix

* revert unneeded changes

* fix

* fix

* fix

* fix style

* fix

* remove 404

* remove log

* fix

* fix

* fix

* fix

* better naming

* parallel testing

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>

* feat: render custom error page for unexpected sever error + remove` pages/_error` (#18606)

* remove page/_error

* refactor app/error

* fix: app/not-found cannot be a static page (#18610)

* Async False (#18611)

* chore: redirect to /500 if pathname does not exist + better error handling (#18615)

* fix lint error

* fix booking page and better error handling

* chore: gracefully handle 404s from pages router's dynamic pages + tests (#18618)

* restore pages/_error

* set custom header in pages/_error

* handle it in middleware

* add test

* remove logs

* better test description

* chore: try using custom 404 in pages/_error (#18622)

* fix: parsing teamId (#18623)

* chore: restore error pages for pages router (#18625)

* disable emails to all guests (#18628)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>

* revert: "feat: bulk shorten links with dub.links.createMany (#18539)" (#18587)

This reverts commit 4902c6a0f8.

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* chore: release v4.8.18

* fix: disable sending sms when email is present (#18632)

* fix: disable sending sms when email is present

* fix: update test

* fix: main lint errors (#18634)

* fix: error-page.tsx related lint errors

* fix: lint no continue-on-error

* Adding more lint fixes

* Bring back annotate code linting results

* Bring back linting continue-on-error

* Slimline lint

* feat: do not show automation webhooks in webhook list (#18607)

* fix: make ESLint work and fix lint errors that were undetected before (#18639)

* fix eslint config

* fix

* add it to dev dep

* fix

* sync eslint version

* force ts-node to compile our ESLint plugin's TS files into CommonJS (which ESLint requires)

* fix some lint errors

* fix lint errors

* remove duplicate classname

* make @typescript-eslint/ban-types a warn for packages/trpc files

* fix lint errors in trpc

* fix lint errors in trpc - 2

* fix

* fix

* fix lint warnings

* chore: clean up config.matcher in middleware (#18638)

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* Allow lint to error but continue (unblock pipeline)

* fix: potential fix for flaky layout shift (#18651)

* potential fix: layout shift

* fix lint error

* feat: update translations via @replexica (#18598)

* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>

* Update yarn.lock

* nit: let tasker handle payload stringification

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Morgan Vernay <morgan@cal.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Nizzy <140507264+nizzyabi@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: GitHub Actions <github-actions@github.com>
Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: Calcom Bot <109866826+calcom-bot@users.noreply.github.com>
Co-authored-by: Replexica <support@replexica.com>
2025-01-16 22:07:30 -05:00

145 lines
4.2 KiB
TypeScript

import type { Prisma } from "@prisma/client";
import { eventTypeAppCardZod } from "@calcom/app-store/eventTypeAppCardZod";
import logger from "@calcom/lib/logger";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import buildCalendarEvent from "./lib/buildCalendarEvent";
import { createCRMEventSchema } from "./schema";
const log = logger.getSubLogger({ prefix: [`[[tasker] createCRMEvent`] });
export async function createCRMEvent(payload: string): Promise<void> {
try {
const parsedPayload = createCRMEventSchema.safeParse(JSON.parse(payload));
if (!parsedPayload.success) {
throw new Error(`malformed payload in createCRMEvent: ${parsedPayload.error}`);
}
const { bookingUid } = parsedPayload.data;
const booking = await prisma.booking.findUnique({
where: {
uid: bookingUid,
status: BookingStatus.ACCEPTED,
},
include: {
user: {
select: {
name: true,
email: true,
locale: true,
username: true,
timeZone: true,
},
},
eventType: {
select: {
metadata: true,
},
},
references: {
select: {
type: true,
},
},
},
});
if (!booking) {
throw new Error(`booking not found for uid: ${bookingUid}`);
}
if (!booking.user) {
throw new Error(`user not found for uid: ${bookingUid}`);
}
const eventTypeMetadata = EventTypeMetaDataSchema.safeParse(booking.eventType?.metadata);
if (!eventTypeMetadata.success) {
throw new Error(`malformed event type metadata: ${eventTypeMetadata.error}`);
}
const eventTypeAppMetadata = eventTypeMetadata.data?.apps;
if (!eventTypeAppMetadata) {
throw new Error(`event type app metadata not found for booking ${bookingUid}`);
}
const calendarEvent = await buildCalendarEvent(bookingUid);
const bookingReferencesToCreate: Prisma.BookingReferenceUncheckedCreateInput[] = [];
// Find enabled CRM apps for the event type
for (const appSlug of Object.keys(eventTypeAppMetadata)) {
try {
const appData = eventTypeAppMetadata[appSlug as keyof typeof eventTypeAppMetadata];
const appParse = eventTypeAppCardZod.safeParse(appData);
if (!appParse.success) {
log.error(`Error parsing event type app data for bookingUid ${bookingUid}`, appParse?.error);
continue;
}
const app = appParse.data;
if (
!app.appCategories ||
!app.appCategories.some((category: string) => category === "crm") ||
!app.enabled ||
!app.credentialId
)
continue;
const crmCredential = await prisma.credential.findUnique({
where: {
id: app.credentialId,
},
include: {
user: {
select: {
email: true,
},
},
},
});
if (!crmCredential) {
throw new Error(`Credential not found for credentialId: ${app.credentialId}`);
}
const CrmManager = (await import("@calcom/core/crmManager/crmManager")).default;
const crm = new CrmManager(crmCredential, app);
const results = await crm.createEvent(calendarEvent).catch((error) => {
log.error(`Error creating crm event for credentialId ${app.credentialId}`, error);
});
if (results) {
bookingReferencesToCreate.push({
type: crmCredential.type,
uid: results.id,
meetingId: results.id,
credentialId: crmCredential.id,
bookingId: booking.id,
});
}
} catch (error) {
log.error(`Error processing CRM app ${appSlug} for booking ${bookingUid}:`, error);
// Continue with next app even if one fails
continue;
}
}
await prisma.bookingReference.createMany({
data: bookingReferencesToCreate,
});
} catch (error) {
log.error(`Error in createCRMEvent for payload: ${payload}:`, error);
throw error;
}
}