-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Zoho Calendar #8144
feat: Zoho Calendar #8144
Changes from 26 commits
68ab3bf
3d7a699
8a51e76
c64440e
3d8c819
9ee3b12
6b861fd
b046b4e
e6c3772
10bf10d
d56a491
a91e12f
2cfc402
62c38e9
0e65ec8
86c235f
249b54c
29e7384
37518ee
5935818
735d9bf
436020e
0451429
0788823
6ce4754
1cb5334
86e00c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auto-generated files shouldn't be committed. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auto-generated files shouldn't be committed. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Auto-generated files shouldn't be committed. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
items: | ||
- ZCal1.jpg | ||
--- | ||
|
||
Zoho Calendar is an online business calendar that makes scheduling easy for you. You can use it to stay on top of your schedule and also share calendars with your team to keep everyone on the same page. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sydwardrae @Jaibles Please review App description |
murtajaziad marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this approach of adding instructions in the app package. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
## Zoho Calendar | ||
|
||
### Obtaining Zoho Calendar 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. From within the API console page, go to "Applications". | ||
3. Click "ADD CLIENT" button top right and select "Server-based Applications". | ||
4. Fill in any information you want in the "Client Details" tab | ||
5. Go to tab "Client Secret" tab. | ||
6. Now copy the Client ID and Client Secret into your app keys in the Cal.com admin panel (`<Cal.com>/settings/admin/apps`). | ||
7. Back in Zoho API Console, set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/zohocalendar/callback` replacing Cal.com URL with the URI at which your application runs. | ||
8. In the "Settings" section check the "Multi-DC" option if you wish to use the same OAuth credentials for all data centers. | ||
9. Click the "Save"/ "UPDATE" button at the bottom footer. | ||
10. You're good to go. Now you can easily add your Zoho Calendar integration in the Cal.com settings. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { NextApiRequest, NextApiResponse } from "next"; | ||
import { stringify } from "querystring"; | ||
|
||
import { WEBAPP_URL } from "@calcom/lib/constants"; | ||
import { defaultHandler, defaultResponder } from "@calcom/lib/server"; | ||
|
||
import { encodeOAuthState } from "../../_utils/encodeOAuthState"; | ||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; | ||
import config from "../config.json"; | ||
import { appKeysSchema as zohoKeysSchema } from "../zod"; | ||
|
||
async function getHandler(req: NextApiRequest, res: NextApiResponse) { | ||
const appKeys = await getAppKeysFromSlug(config.slug); | ||
const { client_id } = zohoKeysSchema.parse(appKeys); | ||
|
||
const state = encodeOAuthState(req); | ||
|
||
const params = { | ||
client_id, | ||
response_type: "code", | ||
redirect_uri: WEBAPP_URL + "/api/integrations/zohocalendar/callback", | ||
scope: [ | ||
"ZohoCalendar.calendar.ALL", | ||
"ZohoCalendar.event.ALL", | ||
"ZohoCalendar.freebusy.READ", | ||
"AaaServer.profile.READ", | ||
], | ||
access_type: "offline", | ||
state, | ||
prompt: "consent", | ||
}; | ||
|
||
const query = stringify(params); | ||
|
||
res.status(200).json({ url: `https://accounts.zoho.com/oauth/v2/auth?${query}` }); | ||
} | ||
|
||
export default defaultHandler({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrapped up endpoints in defaultHandler |
||
GET: Promise.resolve({ default: defaultResponder(getHandler) }), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import type { NextApiRequest, NextApiResponse } from "next"; | ||
import { stringify } from "querystring"; | ||
import { z } from "zod"; | ||
|
||
import { WEBAPP_URL } from "@calcom/lib/constants"; | ||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; | ||
import logger from "@calcom/lib/logger"; | ||
import { defaultHandler, defaultResponder } from "@calcom/lib/server"; | ||
import prisma from "@calcom/prisma"; | ||
|
||
import { decodeOAuthState } from "../../_utils/decodeOAuthState"; | ||
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; | ||
import getInstalledAppPath from "../../_utils/getInstalledAppPath"; | ||
import config from "../config.json"; | ||
import type { ZohoAuthCredentials } from "../types/ZohoCalendar"; | ||
|
||
const log = logger.getChildLogger({ prefix: [`[[zohocalendar/api/callback]`] }); | ||
|
||
const zohoKeysSchema = z.object({ | ||
hariombalhara marked this conversation as resolved.
Show resolved
Hide resolved
|
||
client_id: z.string(), | ||
client_secret: z.string(), | ||
}); | ||
|
||
async function getHandler(req: NextApiRequest, res: NextApiResponse) { | ||
const { code } = req.query; | ||
const state = decodeOAuthState(req); | ||
|
||
if (code && typeof code !== "string") { | ||
res.status(400).json({ message: "`code` must be a string" }); | ||
return; | ||
} | ||
|
||
if (!req.session?.user?.id) { | ||
return res.status(401).json({ message: "You must be logged in to do this" }); | ||
} | ||
|
||
const appKeys = await getAppKeysFromSlug(config.slug); | ||
const { client_id, client_secret } = zohoKeysSchema.parse(appKeys); | ||
|
||
const params = { | ||
client_id, | ||
grant_type: "authorization_code", | ||
client_secret, | ||
redirect_uri: `${WEBAPP_URL}/api/integrations/${config.slug}/callback`, | ||
code, | ||
}; | ||
|
||
const query = stringify(params); | ||
|
||
const response = await fetch(`https://accounts.zoho.com/oauth/v2/token?${query}`, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json; charset=utf-8", | ||
}, | ||
}); | ||
|
||
const responseBody = await response.json(); | ||
console.log(responseBody); | ||
|
||
if (!response.ok || responseBody.error) { | ||
log.error("get access_token failed", responseBody); | ||
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody)); | ||
} | ||
|
||
const key: ZohoAuthCredentials = { | ||
access_token: responseBody.access_token, | ||
refresh_token: responseBody.refresh_token, | ||
expires_in: Math.round(+new Date() / 1000 + responseBody.expires_in), | ||
}; | ||
|
||
await prisma.credential.create({ | ||
data: { | ||
type: config.type, | ||
key, | ||
userId: req.session.user.id, | ||
appId: config.slug, | ||
}, | ||
}); | ||
|
||
res.redirect( | ||
getSafeRedirectUrl(state?.returnTo) ?? getInstalledAppPath({ variant: config.variant, slug: config.slug }) | ||
); | ||
} | ||
|
||
export default defaultHandler({ | ||
GET: Promise.resolve({ default: defaultResponder(getHandler) }), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as add } from "./add"; | ||
export { default as callback } from "./callback"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "Zoho Calendar", | ||
"description": "Zoho Calendar is an online business calendar that makes scheduling easy for you. You can use it to stay on top of your schedule and also share calendars with your team to keep everyone on the same page.", | ||
"slug": "zohocalendar", | ||
"type": "zoho_calendar", | ||
"title": "Zoho Calendar", | ||
"variant": "calendar", | ||
"category": "calendar", | ||
"categories": [ | ||
"calendar" | ||
], | ||
"logo": "icon.svg", | ||
"publisher": "Cal.com", | ||
"url": "https://cal.com/", | ||
"email": "[email protected]" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * as api from "./api"; | ||
export * as lib from "./lib"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Auto-generated files shouldn't be committed.