feat: Init Cal.com Salesforce package#26330
Conversation
- getById - Init updateIncludeRulesAndMappings
- RuleBuilder.tsx: Use unique IDs for React keys instead of array index to prevent reconciliation bugs when removing conditions - updateAttributeSync.handler.ts: Add early organization check before service calls for consistency - createAttributeSync.handler.ts: Add CredentialNotFoundError class for type-safe error handling instead of string matching - IntegrationAttributeSyncService.test.ts: Replace expect.fail() with proper Vitest rejects.toSatisfy() pattern Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
There was a problem hiding this comment.
5 issues found across 15 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/salesforce/api/user-sync.ts">
<violation number="1" location="packages/app-store/salesforce/api/user-sync.ts:14">
P1: Rule violated: **Avoid Logging Sensitive Information**
`log.info` writes the raw request payload (including `email` and potentially sensitive `changedFields`) to application logs, exposing PII in violation of the logging policy. Remove or mask sensitive fields before logging.</violation>
</file>
<file name="packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/CalComCalloutQueueable.cls">
<violation number="1" location="packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/CalComCalloutQueueable.cls:14">
P2: Sequential HTTP callouts in a loop may cause performance issues or timeout errors with multiple payloads. Consider checking Limits.getCallouts() before each callout and potentially chaining to a new Queueable job if the limit is approached, or batch payloads into a single API request if the endpoint supports it.</violation>
</file>
<file name="packages/app-store/salesforce/sfdc-package/force-app/main/default/namedCredentials/CalCom_Production.namedCredential-meta.xml">
<violation number="1" location="packages/app-store/salesforce/sfdc-package/force-app/main/default/namedCredentials/CalCom_Production.namedCredential-meta.xml:6">
P0: Critical security vulnerability: Named Credential sends sensitive user data without authentication. The configuration uses principalType="Anonymous" and generateAuthorizationHeader="false", and the callout code doesn't add any Authorization header or API key. This allows anyone who discovers the endpoint to send unauthorized data to Cal.com. Add authentication using either Named Credential's per-user or per-org authentication, or implement API key authentication in the HTTP request headers.</violation>
</file>
<file name="packages/app-store/salesforce/package.json">
<violation number="1" location="packages/app-store/salesforce/package.json:12">
P2: The new script is named `scratch-org:open` while our README and workflow instructions expect `scratch-org:start`, so the documented `yarn scratch-org:start` command still fails. Rename or alias the script so the documented command works.</violation>
</file>
<file name="packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/UserUpdateHandler.cls">
<violation number="1" location="packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/UserUpdateHandler.cls:35">
P1: Change detection misses cleared/nullified fields. When a User field is set to null, it won't appear in `newFields.keySet()` (since `getPopulatedFieldsAsMap()` only returns populated fields), so the change won't be detected. This causes field deletions to not sync to Cal.com.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| <fullName>CalCom_Production</fullName> | ||
| <label>Cal.com Production</label> | ||
| <endpoint>https://app.cal.com</endpoint> | ||
| <principalType>Anonymous</principalType> |
There was a problem hiding this comment.
P0: Critical security vulnerability: Named Credential sends sensitive user data without authentication. The configuration uses principalType="Anonymous" and generateAuthorizationHeader="false", and the callout code doesn't add any Authorization header or API key. This allows anyone who discovers the endpoint to send unauthorized data to Cal.com. Add authentication using either Named Credential's per-user or per-org authentication, or implement API key authentication in the HTTP request headers.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/namedCredentials/CalCom_Production.namedCredential-meta.xml, line 6:
<comment>Critical security vulnerability: Named Credential sends sensitive user data without authentication. The configuration uses principalType="Anonymous" and generateAuthorizationHeader="false", and the callout code doesn't add any Authorization header or API key. This allows anyone who discovers the endpoint to send unauthorized data to Cal.com. Add authentication using either Named Credential's per-user or per-org authentication, or implement API key authentication in the HTTP request headers.</comment>
<file context>
@@ -0,0 +1,12 @@
+ <fullName>CalCom_Production</fullName>
+ <label>Cal.com Production</label>
+ <endpoint>https://app.cal.com</endpoint>
+ <principalType>Anonymous</principalType>
+ <protocol>Custom</protocol>
+ <allowMergeFieldsInBody>false</allowMergeFieldsInBody>
</file context>
There was a problem hiding this comment.
It is planned for stacked PR I believe
| for (String fieldName : newFields.keySet()) { | ||
| Object oldValue = oldFields.get(fieldName); | ||
| Object newValue = newFields.get(fieldName); | ||
|
|
||
| if (oldValue != newValue) { | ||
| changedFields.put(fieldName, newValue); | ||
| } |
There was a problem hiding this comment.
P1: Change detection misses cleared/nullified fields. When a User field is set to null, it won't appear in newFields.keySet() (since getPopulatedFieldsAsMap() only returns populated fields), so the change won't be detected. This causes field deletions to not sync to Cal.com.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/UserUpdateHandler.cls, line 35:
<comment>Change detection misses cleared/nullified fields. When a User field is set to null, it won't appear in `newFields.keySet()` (since `getPopulatedFieldsAsMap()` only returns populated fields), so the change won't be detected. This causes field deletions to not sync to Cal.com.</comment>
<file context>
@@ -0,0 +1,46 @@
+ Map<String, Object> oldFields = oldUser.getPopulatedFieldsAsMap();
+ Map<String, Object> newFields = newUser.getPopulatedFieldsAsMap();
+
+ for (String fieldName : newFields.keySet()) {
+ Object oldValue = oldFields.get(fieldName);
+ Object newValue = newFields.get(fieldName);
</file context>
Fix confidence (alpha): 8/10
| for (String fieldName : newFields.keySet()) { | |
| Object oldValue = oldFields.get(fieldName); | |
| Object newValue = newFields.get(fieldName); | |
| if (oldValue != newValue) { | |
| changedFields.put(fieldName, newValue); | |
| } | |
| Set<String> allFields = new Set<String>(); | |
| allFields.addAll(oldFields.keySet()); | |
| allFields.addAll(newFields.keySet()); | |
| for (String fieldName : allFields) { | |
| Object oldValue = oldFields.get(fieldName); | |
| Object newValue = newFields.get(fieldName); |
| public void execute(QueueableContext context) { | ||
| String namedCredential = getNamedCredential(); | ||
|
|
||
| for (UserChangePayload payload : payloads) { |
There was a problem hiding this comment.
P2: Sequential HTTP callouts in a loop may cause performance issues or timeout errors with multiple payloads. Consider checking Limits.getCallouts() before each callout and potentially chaining to a new Queueable job if the limit is approached, or batch payloads into a single API request if the endpoint supports it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/salesforce/sfdc-package/force-app/main/default/classes/CalComCalloutQueueable.cls, line 14:
<comment>Sequential HTTP callouts in a loop may cause performance issues or timeout errors with multiple payloads. Consider checking Limits.getCallouts() before each callout and potentially chaining to a new Queueable job if the limit is approached, or batch payloads into a single API request if the endpoint supports it.</comment>
<file context>
@@ -0,0 +1,54 @@
+ public void execute(QueueableContext context) {
+ String namedCredential = getNamedCredential();
+
+ for (UserChangePayload payload : payloads) {
+ sendPayload(namedCredential, payload);
+ }
</file context>
There was a problem hiding this comment.
Valid point. I think we should batch the payloads if possible and send multiple of them in a single salesforce/user-sync request.
Queueable seems to have a limit of 100 callouts
There was a problem hiding this comment.
Thanks for the feedback! I've saved this as a new learning to improve future reviews.
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
- Remove sensitive fields (email, changedFields) from logging in user-sync.ts - Rename scratch-org:open to scratch-org:start in package.json to match README Co-Authored-By: unknown <>
| @@ -1,8 +1,20 @@ | |||
| ## Working With GraphQL | |||
| # Creating a Salesforce test org | |||
| If you have the Salesforce CLI installed, you can create a test org using the following command `yarn scratch-org:create` | |||
There was a problem hiding this comment.
Ah I'll add a point that you need to have the SF CLI tool installed
| if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) { | ||
| System.debug(LoggingLevel.INFO, 'Successfully sent user update to Cal.com for user: ' + payload.salesforceUserId); | ||
| } else { | ||
| System.debug(LoggingLevel.ERROR, 'Failed to send user update to Cal.com. Status: ' + res.getStatusCode() + ' Body: ' + res.getBody()); |
There was a problem hiding this comment.
How do we plan to handle failures in call out? Will we retry them
There was a problem hiding this comment.
That's one option but I think warrants a discussion
hariombalhara
left a comment
There was a problem hiding this comment.
Tested. Seems to work !!

Stacked on #26007
What does this PR do?
This PR creates the Cal.com Salesforce package, to send user update data to Cal.com as a part of the attribute sync feature.
Updates since last revision
Addressed Cubic AI review feedback (confidence >= 9/10):
email,changedFields) from logging inuser-sync.tsto avoid exposing PIIscratch-org:opentoscratch-org:startin package.json to match README documentationItems flagged but not addressed (confidence < 9/10 or requires design decision):
principalType="Anonymous"- authentication will be handled in a stacked PR via instanceUrl + orgId validation against stored credentials (see TODO in user-sync.ts)Visual Demo (For contributors especially)
https://www.loom.com/share/ec50726997a04027ab0f6dc2f3b1c562
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
yarn scratch-org:createto create a test orgyarn sfdc:deployto deploy the current SF package to the scratch orgyarn scratch-org:startto open the scratch orgChecklist
Link to Devin run: https://app.devin.ai/sessions/03d81257bb5445389176c29823afac1d
Requested by: unknown ()