feat: Add Zoho Bigin app to appstore (#8158)
* feat: add zoho bigin app to appstore * Handle appKeys save and read correctly * Fixes * Remove env variables and seeding * Create README for zoho-bigin and link to that in main README * Remove imageSrc, thats deprecated * Improved logs * Remove stray beecepter fetch * Add Zoho Bigin package.json description * Load constants from config.json * Refactor bigin auth tokens management * Remove try catch * fix refresh token not getting stored properly * load api domain dynamically * Fix reschedule not updating zoho bigin event --------- Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
parent
20f7a5841e
commit
05b42c001f
|
@ -16,11 +16,12 @@
|
|||
# - WEB3
|
||||
# - SALESFORCE
|
||||
# - ZOHOCRM
|
||||
# - ZOHO_BIGIN
|
||||
|
||||
# - APP STORE **********************************************************************************************
|
||||
# ⚠️ ⚠️ ⚠️ THESE WILL BE MIGRATED TO THE DATABASE TO PREVENT AWS's 4KB ENV QUOTA ⚠️ ⚠️ ⚠️
|
||||
# - DAILY.CO VIDEO
|
||||
# Enables Cal Video. to get your key
|
||||
# Enables Cal Video. to get your key
|
||||
# 1. Visit our [Daily.co Partnership Form](https://go.cal.com/daily) and enter your information
|
||||
# 2. From within your dashboard, go to the [developers](https://dashboard.daily.co/developers) tab.
|
||||
# @see https://github.com/calcom/cal.com#obtaining-daily-api-credentials
|
||||
|
|
|
@ -464,6 +464,8 @@ following
|
|||
9. Click the "Save"/ "UPDATE" button at the bottom footer.
|
||||
10. You're good to go. Now you can easily add your ZohoCRM integration in the Cal.com settings.
|
||||
|
||||
### Obtaining Zoho Bigin Client ID and Secret
|
||||
[Follow these steps](./packages/app-store/zoho-bigin/)
|
||||
## Workflows
|
||||
|
||||
### Setting up SendGrid for Email reminders
|
||||
|
|
|
@ -25,6 +25,7 @@ import { appKeysSchema as event_type_app_card_zod_ts } from "./templates/event-t
|
|||
import { appKeysSchema as vital_zod_ts } from "./vital/zod";
|
||||
import { appKeysSchema as wordpress_zod_ts } from "./wordpress/zod";
|
||||
import { appKeysSchema as zapier_zod_ts } from "./zapier/zod";
|
||||
import { appKeysSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod";
|
||||
import { appKeysSchema as zohocrm_zod_ts } from "./zohocrm/zod";
|
||||
import { appKeysSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
|
||||
|
||||
|
@ -52,6 +53,7 @@ export const appKeysSchemas = {
|
|||
vital: vital_zod_ts,
|
||||
wordpress: wordpress_zod_ts,
|
||||
zapier: zapier_zod_ts,
|
||||
"zoho-bigin": zoho_bigin_zod_ts,
|
||||
zohocrm: zohocrm_zod_ts,
|
||||
zoomvideo: zoomvideo_zod_ts,
|
||||
};
|
||||
|
|
|
@ -60,6 +60,7 @@ import whereby_config_json from "./whereby/config.json";
|
|||
import { metadata as wipemycalother__metadata_ts } from "./wipemycalother/_metadata";
|
||||
import wordpress_config_json from "./wordpress/config.json";
|
||||
import { metadata as zapier__metadata_ts } from "./zapier/_metadata";
|
||||
import zoho_bigin_config_json from "./zoho-bigin/config.json";
|
||||
import zohocrm_config_json from "./zohocrm/config.json";
|
||||
import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata";
|
||||
|
||||
|
@ -122,6 +123,7 @@ export const appStoreMetadata = {
|
|||
wipemycalother: wipemycalother__metadata_ts,
|
||||
wordpress: wordpress_config_json,
|
||||
zapier: zapier__metadata_ts,
|
||||
"zoho-bigin": zoho_bigin_config_json,
|
||||
zohocrm: zohocrm_config_json,
|
||||
zoomvideo: zoomvideo__metadata_ts,
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ import { appDataSchema as event_type_app_card_zod_ts } from "./templates/event-t
|
|||
import { appDataSchema as vital_zod_ts } from "./vital/zod";
|
||||
import { appDataSchema as wordpress_zod_ts } from "./wordpress/zod";
|
||||
import { appDataSchema as zapier_zod_ts } from "./zapier/zod";
|
||||
import { appDataSchema as zoho_bigin_zod_ts } from "./zoho-bigin/zod";
|
||||
import { appDataSchema as zohocrm_zod_ts } from "./zohocrm/zod";
|
||||
import { appDataSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
|
||||
|
||||
|
@ -52,6 +53,7 @@ export const appDataSchemas = {
|
|||
vital: vital_zod_ts,
|
||||
wordpress: wordpress_zod_ts,
|
||||
zapier: zapier_zod_ts,
|
||||
"zoho-bigin": zoho_bigin_zod_ts,
|
||||
zohocrm: zohocrm_zod_ts,
|
||||
zoomvideo: zoomvideo_zod_ts,
|
||||
};
|
||||
|
|
|
@ -60,6 +60,7 @@ export const apiHandlers = {
|
|||
wipemycalother: import("./wipemycalother/api"),
|
||||
wordpress: import("./wordpress/api"),
|
||||
zapier: import("./zapier/api"),
|
||||
"zoho-bigin": import("./zoho-bigin/api"),
|
||||
zohocrm: import("./zohocrm/api"),
|
||||
zoomvideo: import("./zoomvideo/api"),
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ const appStore = {
|
|||
plausible: import("./plausible"),
|
||||
salesforce: import("./salesforce"),
|
||||
zohocrm: import("./zohocrm"),
|
||||
"zoho-bigin": import("./zoho-bigin"),
|
||||
sendgrid: import("./sendgrid"),
|
||||
stripepayment: import("./stripepayment"),
|
||||
tandemvideo: import("./tandemvideo"),
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
items:
|
||||
- 1.png
|
||||
---
|
||||
|
||||
{DESCRIPTION}
|
|
@ -0,0 +1,9 @@
|
|||
### Obtaining Zoho Bigin Client ID and Secret
|
||||
|
||||
1. Open [Zoho API Console](https://api-console.zoho.com/) and sign into your account, or create a new one.
|
||||
2. Click "ADD CLIENT" button top right and select "Server-based Applications".
|
||||
3. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/zoho-bigin/callback` replacing Cal.com URL with the URI at which your application runs.
|
||||
4. Go to tab "Client Secret" tab.
|
||||
5. Now copy the Client ID and Client Secret to your .env.appStore file into the `ZOHO_BIGIN_CLIENT_ID` and `ZOHO_BIGIN_CLIENT_SECRET` fields.
|
||||
6. In the "Settings" section check the "Multi-DC" option if you wish to use the same OAuth credentials for all data centers.
|
||||
7. You're good to go. Now you can easily add Zoho Bigin from the Cal.com app store.
|
|
@ -0,0 +1,32 @@
|
|||
import axios from "axios";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
||||
import appConfig from "../config.json";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "GET") {
|
||||
const appKeys = await getAppKeysFromSlug(appConfig.slug);
|
||||
|
||||
const clientId = typeof appKeys.client_id === "string" ? appKeys.client_id : "";
|
||||
if (!clientId) return res.status(400).json({ message: "Zoho Bigin client_id missing." });
|
||||
|
||||
const redirectUri = WEBAPP_URL + `/api/integrations/${appConfig.slug}/callback`;
|
||||
const authUrl = axios.getUri({
|
||||
url: "https://accounts.zoho.com/oauth/v2/auth",
|
||||
params: {
|
||||
scope: appConfig.scope,
|
||||
client_id: clientId,
|
||||
response_type: "code",
|
||||
redirect_uri: redirectUri,
|
||||
access_type: "offline",
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).json({ url: authUrl });
|
||||
return;
|
||||
}
|
||||
res.status(400).json({ message: "Invalid request method." });
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import axios from "axios";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import qs from "qs";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { decodeOAuthState } from "../../_utils/decodeOAuthState";
|
||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
||||
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
|
||||
import appConfig from "../config.json";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { code, "accounts-server": accountsServer } = req.query;
|
||||
|
||||
if (code && typeof code !== "string") {
|
||||
res.status(400).json({ message: "`code` must be a string" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req.session?.user?.id) {
|
||||
res.status(401).json({ message: "You must be logged in to do this" });
|
||||
return;
|
||||
}
|
||||
|
||||
const appKeys = await getAppKeysFromSlug(appConfig.slug);
|
||||
|
||||
const clientId = typeof appKeys.client_id === "string" ? appKeys.client_id : "";
|
||||
const clientSecret = typeof appKeys.client_secret === "string" ? appKeys.client_secret : "";
|
||||
|
||||
if (!clientId) return res.status(400).json({ message: "Zoho Bigin client_id missing." });
|
||||
if (!clientSecret) return res.status(400).json({ message: "Zoho Bigin client_secret missing." });
|
||||
|
||||
const accountsUrl = `${accountsServer}/oauth/v2/token`;
|
||||
const redirectUri = WEBAPP_URL + `/api/integrations/${appConfig.slug}/callback`;
|
||||
|
||||
const formData = {
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
code: code,
|
||||
redirect_uri: redirectUri,
|
||||
grant_type: "authorization_code",
|
||||
};
|
||||
|
||||
const tokenInfo = await axios.post(accountsUrl, qs.stringify(formData), {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||
},
|
||||
});
|
||||
|
||||
tokenInfo.data.expiryDate = Math.round(Date.now() + tokenInfo.data.expires_in);
|
||||
tokenInfo.data.accountServer = accountsServer;
|
||||
|
||||
await prisma.credential.create({
|
||||
data: {
|
||||
type: appConfig.type,
|
||||
key: tokenInfo.data,
|
||||
userId: req.session.user.id,
|
||||
appId: appConfig.slug,
|
||||
},
|
||||
});
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
res.redirect(
|
||||
getSafeRedirectUrl(state?.returnTo) ??
|
||||
getInstalledAppPath({ variant: appConfig.variant, slug: appConfig.slug })
|
||||
);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export { default as add } from "./add";
|
||||
export { default as callback } from "./callback";
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"/*": "Don't modify slug - If required, do it using cli edit command",
|
||||
"name": "Zoho Bigin",
|
||||
"slug": "zoho-bigin",
|
||||
"type": "zoho-bigin_other_calendar",
|
||||
"logo": "zohobigin.svg",
|
||||
"url": "https://cal.com/apps/zoho-bigin",
|
||||
"variant": "other",
|
||||
"categories": ["other"],
|
||||
"publisher": "Cal.com",
|
||||
"email": "help@cal.com",
|
||||
"description": "Bigin easily transforms your day-to-day customer processes into actionable pipelines. From qualifying leads to closing deals to managing important after-sales operations—Bigin connects your different teams to work together so that you can offer the best possible experience to your customers. Say goodbye to missing follow-ups, manual data entry, lack of team communication, and information silos.",
|
||||
"isTemplate": false,
|
||||
"__createdUsingCli": true,
|
||||
"__template": "basic",
|
||||
"scope": "ZohoBigin.modules.events.ALL,ZohoBigin.modules.contacts.ALL"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export * as api from "./api";
|
||||
export * as lib from "./lib";
|
|
@ -0,0 +1,312 @@
|
|||
import axios from "axios";
|
||||
import qs from "qs";
|
||||
|
||||
import { getLocation } from "@calcom/lib/CalEventParser";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type {
|
||||
Calendar,
|
||||
CalendarEvent,
|
||||
EventBusyDate,
|
||||
IntegrationCalendar,
|
||||
NewCalendarEventType,
|
||||
Person,
|
||||
} from "@calcom/types/Calendar";
|
||||
import type { CredentialPayload } from "@calcom/types/Credential";
|
||||
|
||||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
|
||||
import { appKeysSchema } from "../zod";
|
||||
|
||||
export type BiginToken = {
|
||||
scope: string;
|
||||
api_domain: string;
|
||||
expires_in: number;
|
||||
expiryDate: number;
|
||||
token_type: string;
|
||||
access_token: string;
|
||||
accountServer: string;
|
||||
refresh_token: string;
|
||||
};
|
||||
|
||||
export type BiginContact = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export default class BiginCalendarService implements Calendar {
|
||||
private readonly integrationName = "zoho-bigin";
|
||||
private readonly auth: { getToken: () => Promise<BiginToken> };
|
||||
private log: typeof logger;
|
||||
private eventsSlug = "/bigin/v1/Events";
|
||||
private contactsSlug = "/bigin/v1/Contacts";
|
||||
|
||||
constructor(credential: CredentialPayload) {
|
||||
this.auth = this.biginAuth(credential);
|
||||
this.log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] });
|
||||
}
|
||||
|
||||
/***
|
||||
* Authenticate calendar service with Zoho Bigin provided credentials.
|
||||
*/
|
||||
private biginAuth(credential: CredentialPayload) {
|
||||
const credentialKey = credential.key as unknown as BiginToken;
|
||||
const credentialId = credential.id;
|
||||
|
||||
const isTokenValid = (token: BiginToken) =>
|
||||
token.access_token && token.expiryDate && token.expiryDate > Date.now();
|
||||
|
||||
return {
|
||||
getToken: () =>
|
||||
isTokenValid(credentialKey)
|
||||
? Promise.resolve(credentialKey)
|
||||
: this.refreshAccessToken(credentialId, credentialKey),
|
||||
};
|
||||
}
|
||||
|
||||
/***
|
||||
* Fetches a new access token if stored token is expired.
|
||||
*/
|
||||
private async refreshAccessToken(credentialId: number, credentialKey: BiginToken) {
|
||||
this.log.debug("Refreshing token as it's invalid");
|
||||
const grantType = "refresh_token";
|
||||
const accountsUrl = `${credentialKey.accountServer}/oauth/v2/token`;
|
||||
|
||||
const appKeys = await getAppKeysFromSlug(this.integrationName);
|
||||
|
||||
const { client_id: clientId, client_secret: clientSecret } = appKeysSchema.parse(appKeys);
|
||||
|
||||
const formData = {
|
||||
grant_type: grantType,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
refresh_token: credentialKey.refresh_token,
|
||||
};
|
||||
|
||||
const tokenInfo = await axios.post(accountsUrl, qs.stringify(formData), {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||
},
|
||||
});
|
||||
|
||||
if (!tokenInfo.data.error) {
|
||||
// set expiry date as offset from current time.
|
||||
tokenInfo.data.expiryDate = Math.round(Date.now() + tokenInfo.data.expires_in);
|
||||
tokenInfo.data.accountServer = credentialKey.accountServer;
|
||||
tokenInfo.data.refresh_token = credentialKey.refresh_token;
|
||||
|
||||
await prisma.credential.update({
|
||||
where: {
|
||||
id: credentialId,
|
||||
},
|
||||
data: {
|
||||
key: tokenInfo.data as BiginToken,
|
||||
},
|
||||
});
|
||||
this.log.debug("Fetched token", tokenInfo.data.access_token);
|
||||
} else {
|
||||
this.log.error(tokenInfo.data);
|
||||
}
|
||||
|
||||
return tokenInfo.data as BiginToken;
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates Zoho Bigin Contact records for every attendee added in event bookings.
|
||||
* Returns the results of all contact creation operations.
|
||||
*/
|
||||
private async createContacts(attendees: Person[]) {
|
||||
const token = await this.auth.getToken();
|
||||
const contacts = attendees.map((attendee) => {
|
||||
const nameParts = attendee.name.split(" ");
|
||||
const firstName = nameParts[0];
|
||||
const lastName = nameParts.length > 1 ? nameParts.slice(1).join(" ") : "-";
|
||||
return {
|
||||
First_Name: firstName,
|
||||
Last_Name: lastName,
|
||||
Email: attendee.email,
|
||||
};
|
||||
});
|
||||
|
||||
return axios({
|
||||
method: "post",
|
||||
url: token.api_domain + this.contactsSlug,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: `Zoho-oauthtoken ${token.access_token}`,
|
||||
},
|
||||
data: JSON.stringify({ data: contacts }),
|
||||
});
|
||||
}
|
||||
|
||||
/***
|
||||
* Finds existing Zoho Bigin Contact record based on email address. Returns a list of contacts objects that matched.
|
||||
*/
|
||||
private async contactSearch(event: CalendarEvent) {
|
||||
const token = await this.auth.getToken();
|
||||
const searchCriteria =
|
||||
"(" + event.attendees.map((attendee) => `(Email:equals:${encodeURI(attendee.email)})`).join("or") + ")";
|
||||
|
||||
return await axios({
|
||||
method: "get",
|
||||
url: `${token.api_domain}${this.contactsSlug}/search?criteria=${searchCriteria}`,
|
||||
headers: {
|
||||
authorization: `Zoho-oauthtoken ${token.access_token}`,
|
||||
},
|
||||
})
|
||||
.then((data) => data.data)
|
||||
.catch((e) => this.log.error("Error searching contact:", JSON.stringify(e), e.response?.data));
|
||||
}
|
||||
|
||||
/***
|
||||
* Sends request to Zoho Bigin API to add new Events.
|
||||
*/
|
||||
private async createBiginEvent(event: CalendarEvent) {
|
||||
const token = await this.auth.getToken();
|
||||
const biginEvent = {
|
||||
Event_Title: event.title,
|
||||
Start_DateTime: toISO8601String(new Date(event.startTime)),
|
||||
End_DateTime: toISO8601String(new Date(event.endTime)),
|
||||
Description: event.additionalNotes,
|
||||
Location: getLocation(event),
|
||||
};
|
||||
|
||||
return axios({
|
||||
method: "post",
|
||||
url: token.api_domain + this.eventsSlug,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: `Zoho-oauthtoken ${token.access_token}`,
|
||||
},
|
||||
data: JSON.stringify({ data: [biginEvent] }),
|
||||
})
|
||||
.then((data) => data.data)
|
||||
.catch((e) => this.log.error("Error creating bigin event", JSON.stringify(e), e.response?.data));
|
||||
}
|
||||
|
||||
/***
|
||||
* Handles orchestrating the creation of new events in Zoho Bigin.
|
||||
*/
|
||||
async handleEventCreation(event: CalendarEvent, contacts: CalendarEvent["attendees"]) {
|
||||
const meetingEvent = await this.createBiginEvent(event);
|
||||
if (meetingEvent.data && meetingEvent.data.length && meetingEvent.data[0].status === "success") {
|
||||
this.log.debug("event:creation:ok", { meetingEvent });
|
||||
return Promise.resolve({
|
||||
uid: meetingEvent.data[0].details.id,
|
||||
id: meetingEvent.data[0].details.id,
|
||||
//FIXME: `externalCalendarId` is required by the `updateAllCalendarEvents` method, but is not used by zoho-bigin App. Not setting this property actually skips calling updateEvent..
|
||||
// Here the value doesn't matter. We just need to set it to something.
|
||||
externalCalendarId: "NO_CALENDAR_ID_NEEDED",
|
||||
type: this.integrationName,
|
||||
password: "",
|
||||
url: "",
|
||||
additionalInfo: { contacts, meetingEvent },
|
||||
});
|
||||
}
|
||||
this.log.debug("meeting:creation:notOk", { meetingEvent, event, contacts });
|
||||
return Promise.reject("Something went wrong when creating a meeting in Zoho Bigin");
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates contacts and event records for new bookings.
|
||||
* Initially creates all new attendees as contacts, then creates the event.
|
||||
*/
|
||||
async createEvent(event: CalendarEvent): Promise<NewCalendarEventType> {
|
||||
const contacts = (await this.contactSearch(event))?.data || [];
|
||||
|
||||
const existingContacts = contacts.map((contact: BiginContact) => contact.email);
|
||||
const newContacts: Person[] = event.attendees.filter(
|
||||
(attendee) => !existingContacts.includes(attendee.email)
|
||||
);
|
||||
|
||||
if (newContacts.length === 0) {
|
||||
return await this.handleEventCreation(event, event.attendees);
|
||||
}
|
||||
|
||||
const createContacts = await this.createContacts(newContacts);
|
||||
if (createContacts.data?.data[0].status === "success") {
|
||||
return await this.handleEventCreation(event, event.attendees);
|
||||
}
|
||||
|
||||
return Promise.reject({
|
||||
calError: "Something went wrong when creating non-existing attendees in Zoho Bigin",
|
||||
});
|
||||
}
|
||||
|
||||
/***
|
||||
* Updates an existing event in Zoho Bigin.
|
||||
*/
|
||||
async updateEvent(uid: string, event: CalendarEvent): Promise<NewCalendarEventType> {
|
||||
this.log.debug(`Updating Event with uid ${uid}`);
|
||||
const token = await this.auth.getToken();
|
||||
const biginEvent = {
|
||||
id: uid,
|
||||
Event_Title: event.title,
|
||||
Start_DateTime: toISO8601String(new Date(event.startTime)),
|
||||
End_DateTime: toISO8601String(new Date(event.endTime)),
|
||||
Description: event.additionalNotes,
|
||||
Location: getLocation(event),
|
||||
};
|
||||
|
||||
return axios
|
||||
.put(token.api_domain + this.eventsSlug, JSON.stringify({ data: [biginEvent] }), {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: `Zoho-oauthtoken ${token.access_token}`,
|
||||
},
|
||||
})
|
||||
.then((data) => data.data)
|
||||
.catch((e) => {
|
||||
this.log.error("Error in updating bigin event", JSON.stringify(e), e.response?.data);
|
||||
});
|
||||
}
|
||||
|
||||
async deleteEvent(uid: string): Promise<void> {
|
||||
const token = await this.auth.getToken();
|
||||
return axios
|
||||
.delete(`${token.api_domain}${this.eventsSlug}?ids=${uid}`, {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: `Zoho-oauthtoken ${token.access_token}`,
|
||||
},
|
||||
})
|
||||
.then((data) => data.data)
|
||||
.catch((e) => this.log.error("Error deleting bigin event", JSON.stringify(e), e.response?.data));
|
||||
}
|
||||
|
||||
async getAvailability(
|
||||
_dateFrom: string,
|
||||
_dateTo: string,
|
||||
_selectedCalendars: IntegrationCalendar[]
|
||||
): Promise<EventBusyDate[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
async listCalendars(_event?: CalendarEvent): Promise<IntegrationCalendar[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
const toISO8601String = (date: Date) => {
|
||||
const tzo = -date.getTimezoneOffset(),
|
||||
dif = tzo >= 0 ? "+" : "-",
|
||||
pad = function (num: number) {
|
||||
return (num < 10 ? "0" : "") + num;
|
||||
};
|
||||
|
||||
return (
|
||||
date.getFullYear() +
|
||||
"-" +
|
||||
pad(date.getMonth() + 1) +
|
||||
"-" +
|
||||
pad(date.getDate()) +
|
||||
"T" +
|
||||
pad(date.getHours()) +
|
||||
":" +
|
||||
pad(date.getMinutes()) +
|
||||
":" +
|
||||
pad(date.getSeconds()) +
|
||||
dif +
|
||||
pad(Math.floor(Math.abs(tzo) / 60)) +
|
||||
":" +
|
||||
pad(Math.abs(tzo) % 60)
|
||||
);
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export { default as CalendarService } from "./CalendarService";
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"name": "@calcom/zoho-bigin",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.ts",
|
||||
"dependencies": {
|
||||
"@calcom/lib": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@calcom/types": "*"
|
||||
},
|
||||
"description": "Bigin easily transforms your day-to-day customer processes into actionable pipelines. From qualifying leads to closing deals to managing important after-sales operations—Bigin connects your different teams to work together so that you can offer the best possible experience to your customers. Say goodbye to missing follow-ups, manual data entry, lack of team communication, and information silos."
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="b" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" width="39.629" height="44" viewBox="0 0 39.629 44">
|
||||
<defs>
|
||||
<style>
|
||||
.d {
|
||||
fill: #039649;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="c" data-name="Layer 1">
|
||||
<path class="d" d="m19.21,44c-.66,0-1.32-.22-1.87-.55-.77-.55-1.32-1.54-1.32-2.53v-9.02L.4,4.73C-.15,3.63-.15,2.42.51,1.54c.55-.99,1.65-1.54,2.75-1.54h28.6c1.099,0,2.089.55,2.639,1.54.66.991.66,2.31.11,3.301l-1.87,3.3h3.74c1.1,0,2.089.55,2.639,1.54.66.99.66,2.31.11,3.3l-10.89,18.81v6.82c0,1.43-.88,2.53-2.2,2.97l-5.94,2.31c-.22.11-.66.11-.99.11M3.26,3.08q-.11.11-.11.221l15.51,27.059c.44.77.44,1.21.44,1.65v8.799s.11.11.22,0l5.94-2.31h.22v-6.6c0-.44,0-1.21.441-1.76l10.78-18.7c0-.11,0-.22-.11-.22l-19.69-.11,5.28,9.13,3.74-6.49c.44-.77,1.32-.99,2.09-.55.77.44.99,1.32.549,2.09l-4.399,7.59c-.991,1.54-3.19,1.43-4.071.11l-6.38-11.33c-.44-.66-.44-1.54,0-2.31.441-.77,1.211-1.32,2.09-1.32h13.42l2.75-4.73s0-.11-.11-.221H3.26Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1021 B |
|
@ -0,0 +1,7 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const appKeysSchema = z.object({
|
||||
client_id: z.string().min(1),
|
||||
client_secret: z.string().min(1),
|
||||
});
|
||||
export const appDataSchema = z.object({});
|
|
@ -280,7 +280,7 @@ export const createEvent = async (
|
|||
if (error?.calError) {
|
||||
calError = error.calError;
|
||||
}
|
||||
log.error("createEvent failed", error, calEvent);
|
||||
log.error("createEvent failed", JSON.stringify(error), calEvent);
|
||||
// @TODO: This code will be off till we can investigate an error with it
|
||||
//https://github.com/calcom/cal.com/issues/3949
|
||||
// await sendBrokenIntegrationEmail(calEvent, "calendar");
|
||||
|
|
|
@ -5165,6 +5165,15 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/zoho-bigin@workspace:packages/app-store/zoho-bigin":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/zoho-bigin@workspace:packages/app-store/zoho-bigin"
|
||||
dependencies:
|
||||
"@calcom/lib": "*"
|
||||
"@calcom/types": "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@calcom/zohocrm@workspace:packages/app-store/zohocrm":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@calcom/zohocrm@workspace:packages/app-store/zohocrm"
|
||||
|
|
Loading…
Reference in New Issue
Block a user