feat(pipedrive-crm): remove Revert.dev and add native Pipedrive OAuth2 (fixes #16797, #22492)#26450
feat(pipedrive-crm): remove Revert.dev and add native Pipedrive OAuth2 (fixes #16797, #22492)#26450Karrrthik7 wants to merge 1 commit intocalcom:mainfrom
Conversation
|
@Karrrthik7 is attempting to deploy a commit to the cal-staging Team on Vercel. A member of the Team first needs to authorize it. |
|
|
There was a problem hiding this comment.
5 issues found across 5 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/app-store/pipedrive-crm/api/add.ts">
<violation number="1" location="packages/app-store/pipedrive-crm/api/add.ts:37">
P1: Missing `response_type: "code"` parameter in OAuth authorization URL. This is a required parameter for the OAuth2 authorization code flow and without it, Pipedrive's OAuth authorization will likely fail. See `zoho-bigin/api/add.ts` for the correct pattern.</violation>
</file>
<file name="packages/app-store/pipedrive-crm/components/EventTypeAppCardInterface.tsx">
<violation number="1" location="packages/app-store/pipedrive-crm/components/EventTypeAppCardInterface.tsx:22">
P2: Missing `res.ok` check before parsing JSON. If the API returns an error status (4xx/5xx), the response may not contain `url` and the error won't be surfaced to the user.</violation>
</file>
<file name="packages/app-store/pipedrive-crm/lib/CrmService.ts">
<violation number="1" location="packages/app-store/pipedrive-crm/lib/CrmService.ts:78">
P1: Token refresh failure is silently swallowed. If `refreshAccessToken` fails, `getToken()` returns without a valid token, causing subsequent API calls to fail with expired credentials. Consider re-throwing the error after logging, or validating the token is valid after refresh attempt.</violation>
<violation number="2" location="packages/app-store/pipedrive-crm/lib/CrmService.ts:189">
P2: Unlike other API methods (`createPipedriveEvent`, `updateMeeting`), `deleteMeeting` doesn't check `res.ok` or throw on failure. Delete failures will be silently ignored.</violation>
</file>
<file name="packages/app-store/pipedrive-crm/api/callback.ts">
<violation number="1" location="packages/app-store/pipedrive-crm/api/callback.ts:24">
P1: Missing validation for absent `code` parameter. The current check `code && typeof code !== "string"` passes when `code` is `undefined`, allowing the flow to continue and cast `undefined as string` on line 48. This will cause a confusing 500 error from Pipedrive instead of a clear 400 from your handler. Per early-return preference, validate that code exists.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const params = new URLSearchParams({ | ||
| client_id: String(appKeys.client_id), | ||
| redirect_uri: redirectUri, | ||
| }); |
There was a problem hiding this comment.
P1: Missing response_type: "code" parameter in OAuth authorization URL. This is a required parameter for the OAuth2 authorization code flow and without it, Pipedrive's OAuth authorization will likely fail. See zoho-bigin/api/add.ts for the correct pattern.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/pipedrive-crm/api/add.ts, line 37:
<comment>Missing `response_type: "code"` parameter in OAuth authorization URL. This is a required parameter for the OAuth2 authorization code flow and without it, Pipedrive's OAuth authorization will likely fail. See `zoho-bigin/api/add.ts` for the correct pattern.</comment>
<file context>
@@ -30,8 +32,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
- newTab: true,
+ const redirectUri = `${WEBAPP_URL_FOR_OAUTH}/api/integrations/pipedrive/callback`;
+ const state = encodeOAuthState(req);
+ const params = new URLSearchParams({
+ client_id: String(appKeys.client_id),
+ redirect_uri: redirectUri,
</file context>
| const params = new URLSearchParams({ | |
| client_id: String(appKeys.client_id), | |
| redirect_uri: redirectUri, | |
| }); | |
| const params = new URLSearchParams({ | |
| client_id: String(appKeys.client_id), | |
| redirect_uri: redirectUri, | |
| response_type: "code", | |
| }); |
| setLoading(true); | ||
| const teamId = eventType.team?.id; | ||
| const q = teamId ? `?teamId=${teamId}` : ""; | ||
| const res = await fetch(`/api/integrations/pipedrive/add${q}`); |
There was a problem hiding this comment.
P2: Missing res.ok check before parsing JSON. If the API returns an error status (4xx/5xx), the response may not contain url and the error won't be surfaced to the user.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/pipedrive-crm/components/EventTypeAppCardInterface.tsx, line 22:
<comment>Missing `res.ok` check before parsing JSON. If the API returns an error status (4xx/5xx), the response may not contain `url` and the error won't be surfaced to the user.</comment>
<file context>
@@ -4,24 +4,55 @@ import AppCard from "@calcom/app-store/_components/AppCard";
+ setLoading(true);
+ const teamId = eventType.team?.id;
+ const q = teamId ? `?teamId=${teamId}` : "";
+ const res = await fetch(`/api/integrations/pipedrive/add${q}`);
+ const json = await res.json();
+ if (json?.url) {
</file context>
| const res = await fetch(`/api/integrations/pipedrive/add${q}`); | |
| const res = await fetch(`/api/integrations/pipedrive/add${q}`); | |
| if (!res.ok) { | |
| throw new Error(`Failed to initiate Pipedrive connection: ${res.status}`); | |
| } |
| await fetch(`https://api.pipedrive.com/v1/activities/${uid}`, { | ||
| method: "DELETE", | ||
| headers: headers, | ||
| }; | ||
|
|
||
| return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions); | ||
| headers: { Authorization: `Bearer ${currentToken.access_token}` }, | ||
| }); |
There was a problem hiding this comment.
P2: Unlike other API methods (createPipedriveEvent, updateMeeting), deleteMeeting doesn't check res.ok or throw on failure. Delete failures will be silently ignored.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/pipedrive-crm/lib/CrmService.ts, line 189:
<comment>Unlike other API methods (`createPipedriveEvent`, `updateMeeting`), `deleteMeeting` doesn't check `res.ok` or throw on failure. Delete failures will be silently ignored.</comment>
<file context>
@@ -10,180 +10,195 @@ import type { CredentialPayload } from "@calcom/types/Credential";
- const requestOptions = {
+ await (await this.auth).getToken();
+ const currentToken = this.credential.key as unknown as PipedriveToken;
+ await fetch(`https://api.pipedrive.com/v1/activities/${uid}`, {
method: "DELETE",
- headers: headers,
</file context>
| await fetch(`https://api.pipedrive.com/v1/activities/${uid}`, { | |
| method: "DELETE", | |
| headers: headers, | |
| }; | |
| return await fetch(`${this.revertApiUrl}crm/events/${uid}`, requestOptions); | |
| headers: { Authorization: `Bearer ${currentToken.access_token}` }, | |
| }); | |
| const res = await fetch(`https://api.pipedrive.com/v1/activities/${uid}`, { | |
| method: "DELETE", | |
| headers: { Authorization: `Bearer ${currentToken.access_token}` }, | |
| }); | |
| if (!res.ok) throw new Error("Failed to delete activity in Pipedrive"); |
| } catch (e: unknown) { | ||
| this.log.error(e); |
There was a problem hiding this comment.
P1: Token refresh failure is silently swallowed. If refreshAccessToken fails, getToken() returns without a valid token, causing subsequent API calls to fail with expired credentials. Consider re-throwing the error after logging, or validating the token is valid after refresh attempt.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/pipedrive-crm/lib/CrmService.ts, line 78:
<comment>Token refresh failure is silently swallowed. If `refreshAccessToken` fails, `getToken()` returns without a valid token, causing subsequent API calls to fail with expired credentials. Consider re-throwing the error after logging, or validating the token is valid after refresh attempt.</comment>
<file context>
@@ -10,180 +10,195 @@ import type { CredentialPayload } from "@calcom/types/Credential";
+ // persist updated token in this instance for subsequent API calls
+ this.credential.key = pipedriveRefresh as any;
+ currentToken = { ...currentToken, ...pipedriveRefresh };
+ } catch (e: unknown) {
+ this.log.error(e);
}
</file context>
| } catch (e: unknown) { | |
| this.log.error(e); | |
| } catch (e: unknown) { | |
| this.log.error(e); | |
| throw e; |
| const { code } = req.query; | ||
| const state = decodeOAuthState(req); | ||
|
|
||
| if (code && typeof code !== "string") { |
There was a problem hiding this comment.
P1: Missing validation for absent code parameter. The current check code && typeof code !== "string" passes when code is undefined, allowing the flow to continue and cast undefined as string on line 48. This will cause a confusing 500 error from Pipedrive instead of a clear 400 from your handler. Per early-return preference, validate that code exists.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/pipedrive-crm/api/callback.ts, line 24:
<comment>Missing validation for absent `code` parameter. The current check `code && typeof code !== "string"` passes when `code` is `undefined`, allowing the flow to continue and cast `undefined as string` on line 48. This will cause a confusing 500 error from Pipedrive instead of a clear 400 from your handler. Per early-return preference, validate that code exists.</comment>
<file context>
@@ -1,19 +1,73 @@
+ const { code } = req.query;
+ const state = decodeOAuthState(req);
+
+ if (code && typeof code !== "string") {
+ res.status(400).json({ message: "`code` must be a string" });
+ return;
</file context>
| if (code && typeof code !== "string") { | |
| if (!code || typeof code !== "string") { |
/claim #16797
What does this PR do?
Fixes:
Files changed (key)
Short description of OAuth flow (in-code comments included)
https://oauth.pipedrive.com/oauth/authorize?client_id=<app_key>&redirect_uri=<WEBAPP_URL>/api/integrations/pipedrive/callback&response_type=code&state=
GET /api/integrations/pipedrive/callback?code=...&state=...
Error handling
How to test locally
Environment setup
WEBAPP_URL_FOR_OAUTH=http://localhost:3000
Register Pipedrive OAuth app with redirect URI:
Run
Test steps
Expected behavior
Mandatory Tasks (DO NOT REMOVE)
Checklist
yarn type-check:ci --forceon changed filesNotes / Follow-ups