-
Notifications
You must be signed in to change notification settings - Fork 2.8k
HubSpot - create the contact properties during integration installation #2882
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a consolidated HubSpotApi client, removes legacy per-operation helpers, updates tracking and UI modules to use the new API and renamed constant, extends OAuth scopes, schedules a background task in the HubSpot OAuth callback to batch-create contact properties, and updates the HubSpot app manifest and README. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant CB as /api/hubspot/callback
participant HS as installIntegration
participant BG as waitUntil (background)
participant API as HubSpotApi
note over CB: OAuth callback flow
U->>CB: Complete HubSpot install
CB->>HS: installIntegration()
HS-->>CB: { accessToken, ... }
alt install succeeded
CB->>BG: schedule property creation
BG->>API: new HubSpotApi(token)
BG->>API: createPropertiesBatch(objectType:"0-1", HUBSPOT_DUB_CONTACT_PROPERTIES)
API-->>BG: result
else install failed
CB-->>U: error response
end
CB-->>U: 200/redirect
sequenceDiagram
autonumber
participant E as Event (lead/sale)
participant S as trackLead / trackSale
participant API as HubSpotApi
E->>S: payload + auth token
S->>API: new HubSpotApi(token)
par Fetch HubSpot data
S->>API: getContact(contactId)
S->>API: getDeal(dealId)
end
alt Update needed
S->>API: updateContact({ contactId, properties })
API-->>S: update result
else No update
S-->>E: no-op
end
S-->>E: done
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/web/lib/integrations/hubspot/api.ts (1)
72-72: Align parameter type with contact accessor (accept string IDs as well).Webhook objectIds can arrive as strings; mirroring getContact’s number | string improves ergonomics.
- async getDeal(dealId: number) { + async getDeal(dealId: number | string) {apps/web/lib/integrations/hubspot/track-lead.ts (1)
117-124: Avoid unhandled errors in background update; soften DB lookup.
- _updateHubSpotContact can throw on findUniqueOrThrow; wrap body in try/catch or use findUnique with guard.
- Also prefer building customerName with filter(Boolean) to avoid "null null".
Example refactor (outside the shown ranges):
export const _updateHubSpotContact = async (args: { hubSpotApi: HubSpotApi; contact: HubSpotContact; trackLeadResult: TrackLeadResponse; }) => { try { const { hubSpotApi, contact, trackLeadResult } = args; if (contact.properties.dub_link && contact.properties.dub_partner_email) { console.log(`[HubSpot] Contact ${contact.id} already updated. Skipping.`); return; } const properties: Record<string, string> = {}; if (trackLeadResult.link?.partnerId) { const partner = await prisma.partner.findUnique({ where: { id: trackLeadResult.link.partnerId }, select: { email: true }, }); if (partner?.email) properties["dub_partner_email"] = partner.email; } if (trackLeadResult.link?.shortLink) { properties["dub_link"] = trackLeadResult.link.shortLink; } if (Object.keys(properties).length === 0) return; await hubSpotApi.updateContact({ contactId: contact.id, properties }); } catch (err) { console.error("[HubSpot] Failed to update contact with Dub fields:", err); } };And for building name in the deal branch (outside ranges):
const customerName = [contactInfo.properties.firstname, contactInfo.properties.lastname] .filter(Boolean) .join(" ") || null;Also applies to: 159-163
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/web/app/(ee)/api/hubspot/callback/route.ts(3 hunks)apps/web/lib/integrations/hubspot/api.ts(1 hunks)apps/web/lib/integrations/hubspot/constants.ts(2 hunks)apps/web/lib/integrations/hubspot/get-contact.ts(0 hunks)apps/web/lib/integrations/hubspot/get-deal.ts(0 hunks)apps/web/lib/integrations/hubspot/track-lead.ts(8 hunks)apps/web/lib/integrations/hubspot/track-sale.ts(4 hunks)apps/web/lib/integrations/hubspot/ui/settings.tsx(3 hunks)apps/web/lib/integrations/hubspot/update-contact.ts(0 hunks)packages/hubspot-app/README.md(1 hunks)packages/hubspot-app/src/app/app-hsmeta.json(1 hunks)
💤 Files with no reviewable changes (3)
- apps/web/lib/integrations/hubspot/get-contact.ts
- apps/web/lib/integrations/hubspot/get-deal.ts
- apps/web/lib/integrations/hubspot/update-contact.ts
🧰 Additional context used
📓 Path-based instructions (1)
packages/hubspot-app/**/*-hsmeta.json
📄 CodeRabbit inference engine (packages/hubspot-app/CLAUDE.md)
packages/hubspot-app/**/*-hsmeta.json: Component configuration files must be named with the -hsmeta.json suffix
The uid field in -hsmeta.json files must be unique within the project
The type field in -hsmeta.json defines the component type and must be set correctly
Files:
packages/hubspot-app/src/app/app-hsmeta.json
🧠 Learnings (8)
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/**/-hsmeta.json : If config.distribution is marketplace in the app component, config.auth.type must be oauth
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/**/-hsmeta.json : Any URLs used via hubspot.fetch in settings must be listed in app component config.permittedUrls.fetch
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-17T02:53:28.359Z
Learnt from: devkiran
PR: dubinc/dub#2839
File: apps/web/lib/integrations/hubspot/schema.ts:5-12
Timestamp: 2025-09-17T02:53:28.359Z
Learning: HubSpot's OAuth token response returns `scopes` as an array of strings, not as a space-delimited string. The schema `scopes: z.array(z.string())` in hubSpotAuthTokenSchema is correct for HubSpot's actual API response format.
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/**/*-hsmeta.json : The type field in -hsmeta.json defines the component type and must be set correctly
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/scim/**/-hsmeta.json : There can only be one scim component and it must be under app/scim
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/app/**/-hsmeta.json : Any URLs called via hubspot.fetch in UI extensions must be listed in the app component’s config.permittedUrls.fetch
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Applies to packages/hubspot-app/**/*-hsmeta.json : Component configuration files must be named with the -hsmeta.json suffix
Applied to files:
packages/hubspot-app/src/app/app-hsmeta.json
📚 Learning: 2025-09-19T18:46:43.787Z
Learnt from: CR
PR: dubinc/dub#0
File: packages/hubspot-app/CLAUDE.md:0-0
Timestamp: 2025-09-19T18:46:43.787Z
Learning: Use hs CLI help and debugging flags (e.g., --help, --debug, hs doctor) and the appropriate hs project/account subcommands
Applied to files:
packages/hubspot-app/README.md
🧬 Code graph analysis (5)
apps/web/lib/integrations/hubspot/ui/settings.tsx (1)
apps/web/lib/integrations/hubspot/constants.ts (1)
HUBSPOT_DEFAULT_CLOSED_WON_DEAL_STAGE_ID(28-28)
apps/web/lib/integrations/hubspot/track-sale.ts (2)
apps/web/lib/integrations/hubspot/constants.ts (1)
HUBSPOT_DEFAULT_CLOSED_WON_DEAL_STAGE_ID(28-28)apps/web/lib/integrations/hubspot/api.ts (1)
HubSpotApi(8-135)
apps/web/app/(ee)/api/hubspot/callback/route.ts (3)
apps/web/lib/integrations/install.ts (1)
installIntegration(14-102)apps/web/lib/integrations/hubspot/api.ts (1)
HubSpotApi(8-135)apps/web/lib/integrations/hubspot/constants.ts (1)
HUBSPOT_DUB_CONTACT_PROPERTIES(30-53)
apps/web/lib/integrations/hubspot/api.ts (2)
apps/web/lib/integrations/hubspot/constants.ts (1)
HUBSPOT_API_HOST(11-11)apps/web/lib/integrations/hubspot/schema.ts (2)
hubSpotContactSchema(31-41)hubSpotDealSchema(43-60)
apps/web/lib/integrations/hubspot/track-lead.ts (1)
apps/web/lib/integrations/hubspot/api.ts (1)
HubSpotApi(8-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (8)
apps/web/lib/integrations/hubspot/ui/settings.tsx (2)
9-9: LGTM: renamed constant usage is consistent.Imports and UI placeholder now align with HUBSPOT_DEFAULT_CLOSED_WON_DEAL_STAGE_ID.
18-19: Nit: keep customer-facing placeholder and default in sync with saved settings.Already consistent; no action needed.
Also applies to: 66-67
apps/web/lib/integrations/hubspot/track-sale.ts (1)
44-49: Depends on API fix for deals endpoint path.This will work once HubSpotApi.getDeal uses /objects/deals/{id}. No other changes needed here.
After updating api.ts, sanity-check one event path locally with a sample payload to ensure deal/contact fetches succeed.
apps/web/lib/integrations/hubspot/track-lead.ts (2)
20-27: LGTM: centralizing calls via HubSpotApi.Constructor usage and swapping to hubSpotApi.getContact are sound.
67-87: LGTM: deal branch mirrors contact branch via HubSpotApi.The additional fetch to hydrate contact properties is required since associations omit them.
apps/web/lib/integrations/hubspot/constants.ts (2)
13-19: Scopes update aligns with property-creation needs.Including crm.schemas.contacts.write is required for property schema writes.
28-53: Property definitions look good; no lingering references to the old constant.git grep shows only HUBSPOT_DEFAULT_CLOSED_WON_DEAL_STAGE_ID in apps/web/lib/integrations/hubspot/constants.ts, apps/web/lib/integrations/hubspot/track-sale.ts, and apps/web/lib/integrations/hubspot/ui/settings.tsx — no matches for DEFAULT_CLOSED_WON_DEAL_STAGE_ID. Confirm manifest scope and runtime creation for these contact properties.
packages/hubspot-app/src/app/app-hsmeta.json (1)
18-20: Approve — scope addition OK; hsmeta UIDs unique
- Adding crm.schemas.contacts.write aligns with creating contact properties.
- distribution=marketplace with auth.type=oauth is correct.
- UIDs across *-hsmeta.json are unique (found: dub, dub-webhooks).
Summary by CodeRabbit