Skip to content

Conversation

@saajidAha
Copy link
Contributor

@saajidAha saajidAha commented Nov 14, 2025

This PR introduces the salesforce-sync service, which synchronizes Salesforce Account and Opportunity objects with a specified managed SQL database.

Summary by CodeRabbit

  • New Features
    • Added a Salesforce-to-MySQL sync service that periodically syncs Account and Opportunity data, with guarded parallel-processing checks and shadow-copy triggering.
  • Configuration
    • Project manifest and dependency metadata added; observability-enabled build option configured.
  • Database
    • Added bootstrap SQL for sync tables, logs, and a shadow-copy procedure.
  • Monitoring & Logging
    • Detailed sync status logging (PROCESSING/COMPLETED/FAILED) and error reporting.
  • Documentation
    • Added README and deployment diagram.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Walkthrough

Adds a new Ballerina Salesforce-to-MySQL sync project: project manifests and gitignore, DB schema and stored procedure, types and config, Salesforce client functions, MySQL transactional DB operations, orchestration main loop and utilities, README and deployment diagram; no existing code removed.

Changes

Cohort / File(s) Change Summary
Project config & metadata
\.gitignore, Ballerina.toml, Dependencies.toml
Added gitignore, Ballerina project manifest (observability included), and an auto-generated Dependencies.toml.
Database schema & assets
database/sync_table_creation.sql, documents/salesforce-sync-deployment-diagram.drawio
Added SQL bootstrap creating salesforce_sync DB, tables sf_log, sf_account, sf_opportunity, and stored procedure arr_shadow_copy; added deployment diagram asset.
Documentation
README.md
Added README describing the Salesforce sync service, targets (Account, Opportunity), and behavior.
Core types & config
types.bal, sync_config.bal
Added enums and record types for sync, DB, Salesforce responses and records; added configurable sync schedules and getSyncConfig.
Salesforce integration
sf_functions.bal
Added Salesforce client with timeout and retry policy; sfQueryAccount and sfQueryOpportunity with error handling and streaming-to-typed-array conversion.
Database operations
db_functions.bal
Added MySQL client and DB functions: dbUpdateSync, dbInsertSyncLog, dbCheckProcessing, dbGetLastSyncLog, dbShadowCopy, dbPrepareInitQuery, dbInsertQuery.
Orchestration & utilities
utils.bal, main.bal
Added processSyncObject, checkSyncStatus, handleSalesforceError; added main() and executeSync to orchestrate scheduled syncs and log lifecycle.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Main as Sync Service (main)
    participant Utils as Utils (process/check)
    participant SF as Salesforce Client
    participant DB as MySQL DB

    Main->>Utils: checkSyncStatus(syncObj)
    Utils->>DB: dbGetLastSyncLog(syncObj)
    DB-->>Utils: last successful end_time
    Utils-->>Main: upToDate? (boolean)

    alt Sync required
        Main->>DB: dbInsertSyncLog(PROCESSING)
        DB-->>Main: logIndex
        Main->>Utils: processSyncObject(syncObj)
        Utils->>SF: sfQueryAccount / sfQueryOpportunity
        SF-->>Utils: records
        Utils->>DB: dbUpdateSync(records)
        DB-->>Utils: success
        Main->>DB: dbInsertSyncLog(COMPLETED, logIndex)
    else Skipped or Up-to-date
        Main-->>Main: skip
    end

    Main->>DB: dbShadowCopy()
    DB-->>Main: shadow copy complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Files needing focused review:
    • db_functions.bal — transactional batch logic, generated parameterized SQL and INSERT ... ON DUPLICATE KEY UPDATE correctness, SQL injection risk and parameter binding.
    • main.bal, utils.bal — orchestration, concurrency/processing checks, log lifecycle, and error-handling semantics.
    • sf_functions.bal — Salesforce client retry policy, streaming query handling, and structured error extraction.
    • database/sync_table_creation.sql — schema types, constraints, default values, and stored procedure correctness and delimiter usage.

Poem

🐰 I hop from query to query with SOQL in my paw,
I mirror tables gently and log the sync I saw.
From Account fields to Opportunity, I hum a tiny tune—
Shadow copies tucked and ready beneath the silver moon.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and lacks required template sections including Purpose, Goals, Approach, User Stories, Release Notes, Documentation, Testing details, and other comprehensive information. Complete the PR description using the repository template with detailed sections for Purpose, Goals, Approach, User stories, Release notes, Documentation, Automation tests, Security checks, and other required sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately reflects the main change: implementing a Salesforce Sync Service in the Common Tools repository.
Linked Issues check ✅ Passed The PR successfully implements the Salesforce Sync service by adding codebase for syncing Salesforce Account and Opportunity objects with MySQL database, satisfying issue #2's requirement.
Out of Scope Changes check ✅ Passed All changes are focused on implementing the Salesforce Sync service with database schema, configuration, and sync logic for Account and Opportunity objects as required.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3a5871 and d59f9f3.

📒 Files selected for processing (1)
  • salesforce-to-mysql-data-sync/sync_config.bal (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • salesforce-to-mysql-data-sync/sync_config.bal

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saajidAha saajidAha changed the title [Common Tools][Salesforce-Sync] Migrate Salesforce sync codebase to the Common Tools repository [Common Tools] [Salesforce-Sync] Migrate Salesforce sync codebase to the Common Tools repository Nov 14, 2025
@saajidAha saajidAha marked this pull request as ready for review November 14, 2025 04:53
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (20)
salesforce-sync/README.md (1)

1-4: Good start with minimal documentation.

The README clearly identifies the service purpose and lists supported sync objects. Consider expanding with setup/usage instructions and configuration details in future updates.

salesforce-sync/documents/salesforce-sync-deployment-diagram.drawio (1)

1-1: Deployment diagram is included for operational context.

The diagrams.net file provides deployment architecture visualization. Ensure the diagram remains updated as the sync service evolves.

salesforce-sync/database/sync_table_update_v1.0.1.sql (3)

1-4: Avoid column-level charset/collation specifications.

MySQL Workbench typically adds charset/collation at the column level, but set character set and collation settings at the database level, and remove references to specific character sets or collations at the table or column level. This reduces complexity and prevents mismatches during replication.

Apply this diff to remove the redundant charset/collation:

-ALTER TABLE `salesforce_sync`.`sf_account` 
-CHANGE COLUMN `Name` `Name` VARCHAR(100) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin' NOT NULL ,
-CHANGE COLUMN `Account_Owner_Email` `Account_Owner_Email` VARCHAR(100) NOT NULL ,
-CHANGE COLUMN `Account_Owner_FullName` `Account_Owner_FullName` VARCHAR(100) NOT NULL ;
+ALTER TABLE `salesforce_sync`.`sf_account` 
+CHANGE COLUMN `Name` `Name` VARCHAR(100) NOT NULL,
+CHANGE COLUMN `Account_Owner_Email` `Account_Owner_Email` VARCHAR(100) NOT NULL,
+CHANGE COLUMN `Account_Owner_FullName` `Account_Owner_FullName` VARCHAR(100) NOT NULL;

6-22: Break this large migration into smaller, focused units.

This migration combines sf_account alterations and 17 sf_opportunity schema changes in a single file. Each migration should execute a single unit of work on the database. This comes from the Agile software development principle of introducing small, incremental changes. Small migrations are easier to read and understand.

Consider splitting this into:

  • One migration for sf_account changes.
  • Separate migrations for each distinct set of sf_opportunity changes (e.g., one for CloseDate, one for ARR columns, etc.).

This improves readability, makes rollbacks safer, and aligns with industry best practices.


9-13: Inconsistent default value formats across migrations.

Lines 9–13 use numeric defaults (0.00), while v1.0.4, v1.0.9, and v1.0.11 use string defaults ('0.00'). Line 5 uses tinyint DEFAULT '0' (string for numeric type). Standardize on one convention:

-ADD COLUMN `Cloud_ARR_Opportunity__c` DECIMAL(16,2) NULL DEFAULT 0.00 AFTER `OB_Delayed_ARR__c`,
-ADD COLUMN `IAM_BU_ARR_Opportunity__c` DECIMAL(16,2) NULL DEFAULT 0.00 AFTER `Cloud_ARR_Opportunity__c`,
+ADD COLUMN `Cloud_ARR_Opportunity__c` DECIMAL(16,2) NULL DEFAULT '0.00' AFTER `OB_Delayed_ARR__c`,
+ADD COLUMN `IAM_BU_ARR_Opportunity__c` DECIMAL(16,2) NULL DEFAULT '0.00' AFTER `Cloud_ARR_Opportunity__c`,
salesforce-sync/database/sync_table_update_v1.0.2.sql (1)

1-15: Shadow-copy procedure is correct but consider schema-safety and operational behavior

The procedure does what it says, but two aspects are worth double‑checking:

  • INSERT INTO arr_sf_account SELECT * FROM sf_account (and the opportunity equivalent) rely on CREATE ... LIKE + SELECT *. That’s safe today, but an explicit column list is more robust if future schema changes introduce computed/virtual columns or different column ordering.
  • DROP TABLE / CREATE TABLE for arr_sf_* on every run will drop and recreate indexes and can briefly remove the tables. If these tables are queried concurrently, or the data set is large, you may want to consider TRUNCATE arr_sf_*; INSERT ... instead (if you don't need to change the table definition each run).
salesforce-sync/database/sync_table_update_v1.0.10.sql (1)

1-8: TRUNCATE + new DECIMAL columns – confirm data-loss and nullability expectations

This migration looks structurally fine, but please confirm two things:

  • TRUNCATE salesforce_sync.sf_opportunity irreversibly wipes all existing opportunities in this table. That’s appropriate only if you are guaranteed to fully repopulate from Salesforce on deployment (and no local‑only data lives here).
  • New columns are NULL DEFAULT '0.00'. If you conceptually treat missing values as zero, consider making them NOT NULL DEFAULT 0.00 to simplify downstream logic and avoid needing to handle NULL in queries and application code.
salesforce-sync/main.bal (3)

10-41: Tighten typing between SyncObject and getSyncConfig / checkSyncStatus

The orchestration loop is clear, but the type boundary is a bit loose:

  • supportedSyncObjs is SyncObject[], and you iterate as foreach SyncObject syncObject in supportedSyncObjs.
  • getSyncConfig is declared as function getSyncConfig(string syncObj) and matches on ACCOUNT / OPPORTUNITY.

For better type-safety and IDE support, consider changing getSyncConfig (and any similar helpers) to accept SyncObject instead of string, and keep the match on ACCOUNT/OPPORTUNITY in that type. That removes any chance of passing arbitrary strings and aligns the API with how main and executeSync already use the enum.


42-53: Shadow-copy runs even when per-object sync fails – verify intended behavior

isSyncExecuted is set to true immediately after executeSync(syncObject); and is never reset. Since executeSync handles errors internally and does not propagate them, isSyncExecuted becomes true even if the sync logic for an object failed and only logged/marked FAILED.

That means:

  • dbShadowCopy() is invoked whenever at least one object attempted to sync, not only when all attempted syncs completed successfully.

If the shadow tables (arr_sf_*) are intended as a “last good snapshot”, you may want to:

  • Make executeSync return a boolean or error? indicating success, and
  • Only set isSyncExecuted (or call dbShadowCopy) when all required syncs succeed.

Otherwise, please confirm that taking a shadow copy after a failed sync is acceptable for your downstream consumers.


55-80: executeSync error handling and logging look solid (minor nit only)

The do/on fail pattern, dbCheckProcessing guard, and logging to the sync log table form a good unit of work. The only minor nit is the comment typo (whehter at Line 61), which you can fix at your convenience.

salesforce-sync/sync_config.bal (1)

17-109: Align getSyncConfig parameter with SyncObject enum and central callers

getSyncConfig is currently declared as:

function getSyncConfig(string syncObj) returns SfSyncConf|error {
    match syncObj {
        ACCOUNT => { ... }
        OPPORTUNITY => { ... }
        _ => error ...
    }
}

Given that callers (e.g., main and executeSync) already work with the SyncObject enum, consider:

  • Changing the signature to function getSyncConfig(SyncObject syncObj) returns SfSyncConf|error.
  • Keeping the match arms as ACCOUNT and OPPORTUNITY (now clearly enum members).
  • Letting any stringly-typed callers convert explicitly if needed, instead of the core API taking a plain string.

This improves type-safety and keeps the set of valid values tightly bound to the enum.

salesforce-sync/types.bal (1)

122-232: Verify non-nullable decimal fields in SFOpportunitySyncRecord are always populated

The SFOpportunitySyncRecord definition closely matches the OPPORTUNITY SOQL, which is great. One thing to confirm:

  • Several currency fields are declared as non-nullable decimal (e.g., ARR__c, IAM_ARR__c, APIM_ARR__c, Integration_ARR__c, Open_Banking_ARR__c, Delayed_ARR__c, IAM_Delayed_ARR__c, APIM_Delayed_ARR__c, Integration_Delayed__c, CL_ARR_Today__c), while others are decimal?.

If Salesforce can ever return null for any of the non-nullable ones (e.g., for older or partially filled opportunities), result.cloneWithType() in sf_functions.bal will fail for those records.

Please verify that these fields are guaranteed non-null in your org. If not, it would be safer to mark them as decimal? and adjust downstream code accordingly.

salesforce-sync/sf_functions.bal (2)

35-53: Simplify result handling in sfQueryAccount

You already use check salesforceEP->query(...), so the outer error case is handled at that point. Given that:

stream<SFAccountSyncRecord, error?>|error resultStream =
        check salesforceEP->query(syncConfig.soqlQuery);

if resultStream is error {
    error err = resultStream;
    return handleSalesforceError(err);
}

is effectively dead code. Consider:

stream<SFAccountSyncRecord, error?> resultStream =
        check salesforceEP->query(syncConfig.soqlQuery);

SFAccountSyncRecord[] sfRecords =
    check from var result in resultStream
    select check result.cloneWithType();

and let any per-record stream errors propagate via check as you do now. This reduces branching and keeps error-handling clearer.


55-73: Apply the same simplification pattern to sfQueryOpportunity

The same comment as for sfQueryAccount applies here: once you check the result of salesforceEP->query, you no longer need stream|error or the if resultStream is error guard. Typing it as a plain stream<SFOpportunitySyncRecord, error?> and relying on check in the comprehension keeps the function lean and idiomatic.

salesforce-sync/utils.bal (2)

17-30: Tighten sync object typing / match coverage in processSyncObject

syncObj is a string, but the match only covers ACCOUNT and OPPORTUNITY. It would be safer/clearer to either:

  • Type syncObj as the SyncObject enum (or equivalent constrained type), or
  • Add a default (_) branch that logs and returns an error for unsupported values.

This avoids silent no-ops or future mismatch if new sync object types are introduced.


38-63: Clarify time handling and debug label in checkSyncStatus

Two minor points here:

  1. timeLastSuccess is initialized with [0] and later used in utcDiffSeconds/utcAddSeconds. That works as a "epoch" sentinel, but it’s a bit opaque and tightly couples to the current time:Utc representation. A more explicit approach (e.g., deriving a proper epoch Utc via an API helper) would be clearer and more resilient to library evolution.

  2. The debug messages are labeled [cacheSyncStatus()] while the function is checkSyncStatus. This can confuse log readers.

Consider:

  • Constructing timeLastSuccess via a dedicated helper (or clearly documenting the sentinel usage), and
  • Updating the debug labels to [checkSyncStatus()].
salesforce-sync/db_functions.bal (3)

123-146: Prefer higher‑level time APIs instead of manual Utc tuple arithmetic

hourAgoTime is built via:

time:Utc hourAgoTime = [time:utcNow()[0] - (60 * 60)];

This relies on the internal shape of time:Utc and manual element indexing, which is brittle and harder to read. The intent (“one hour ago”) is clearer and more robust if expressed via the time module’s helpers, e.g.:

-    time:Utc hourAgoTime = [time:utcNow()[0] - (60 * 60)];
+    time:Utc hourAgoTime = time:utcAddSeconds(time:utcNow(), -3600);

It also keeps the representation details fully encapsulated in the time module.


200-209: Correct function name in dbPrepareInitQuery error message

The error message currently mentions dbPrepareBatchQuery() even though this is dbPrepareInitQuery(), which can mislead debugging:

return error(string `Invalid records type: [dbPrepareBatchQuery()] Batch SQL query for passed in record type`
    + string `(${(typeof records).toString()}) not defined.`);

Consider updating the label to reflect the actual function:

-    return error(string `Invalid records type: [dbPrepareBatchQuery()] Batch SQL query for passed in record type`
+    return error(string `Invalid records type: [dbPrepareInitQuery()] Init SQL query for passed-in record type`
         + string `(${(typeof records).toString()}) not defined.`);

This will make log/error messages much easier to interpret.


216-436: Confirm omission of OB_Delayed_ARR__c from opportunity sync mappings

The sf_opportunity table schema (see salesforce-sync/database/sync_table_creation.sql) includes an OB_Delayed_ARR__c column, but dbPrepareBatchQuery’s insert/update templates for SFOpportunitySyncRecord[] do not set or update this field.

If the Salesforce records expose OB_Delayed_ARR__c and it’s important for downstream reporting, you may want to include it in both the INSERT column list/values and the ON DUPLICATE KEY UPDATE clause. If it’s intentionally unused, a short comment indicating that would help future maintainers.

salesforce-sync/database/sync_table_creation.sql (1)

95-105: Shadow copy procedure assumes column parity via SELECT *

arr_shadow_copy uses CREATE TABLE ... LIKE followed by INSERT ... SELECT *:

CREATE TABLE IF NOT EXISTS arr_sf_account LIKE sf_account;
INSERT INTO arr_sf_account SELECT * FROM sf_account;
...
CREATE TABLE IF NOT EXISTS arr_sf_opportunity LIKE sf_opportunity;
INSERT INTO arr_sf_opportunity SELECT * FROM sf_opportunity;

This is fine as long as:

  • The shadow tables are always regenerated from the live tables (as here), and
  • Future schema changes keep the source tables compatible.

If you ever need additional columns or different defaults in the shadow tables, switching to an explicit column list would be safer. For now, a brief comment stating that arr_sf_* are intended to be exact structural clones of sf_* would clarify the design intent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e7e851 and 1007f88.

📒 Files selected for processing (26)
  • salesforce-sync/.gitignore (1 hunks)
  • salesforce-sync/Ballerina.toml (1 hunks)
  • salesforce-sync/CHANGELOG.md (1 hunks)
  • salesforce-sync/Dependencies.toml (1 hunks)
  • salesforce-sync/README.md (1 hunks)
  • salesforce-sync/database/sync_table_creation.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.1.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.10.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.11.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.12.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.13.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.14.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.2.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.4.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.5.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.6.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.7.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.8.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.9.sql (1 hunks)
  • salesforce-sync/db_functions.bal (1 hunks)
  • salesforce-sync/documents/salesforce-sync-deployment-diagram.drawio (1 hunks)
  • salesforce-sync/main.bal (1 hunks)
  • salesforce-sync/sf_functions.bal (1 hunks)
  • salesforce-sync/sync_config.bal (1 hunks)
  • salesforce-sync/types.bal (1 hunks)
  • salesforce-sync/utils.bal (1 hunks)
🧰 Additional context used
🪛 LanguageTool
salesforce-sync/CHANGELOG.md

[style] ~68-~68: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ration record name -> DatabaseConf. - Renamed [types] ConfChoreoApp configuration r...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~69-~69: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...iguration record name -> ChoreoApp. - Renamed [types] ConfErrorEmail configuration ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~76-~76: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t to fetch sync status information. - Removed [types] SyncInfo sync info record. ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~77-~77: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...types] SyncInfo sync info record. - Removed [types] SyncInfoItem sync info item r...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~78-~78: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...yncInfoItemsync info item record. - Removed [types]SyncInfoResponse` info respons...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~79-~79: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...InfoResponseinfo response record. - Removed [types]ServiceErrorDetail` service er...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~80-~80: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tailservice error details record. - Removed [types]BadRequestError 400: Bad Req...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~81-~81: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...400: Bad Request response record. - Removed [types] InternalServerError `500: Int...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~82-~82: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...rnal Server Errorresponse record. - Removed [utils]ackCaller()` HTTP caller ackno...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~83-~83: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...HTTP caller acknowledge function. - Removed [utils]generateInfoPayload()` info pa...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~84-~84: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...()` info payload generation function. - Removed the redundant caching functionalities w...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~85-~85: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... functionalities with the new flow. - Removed [types] CacheKeys cache key enum. -...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~86-~86: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...[types] CacheKeys cache key enum. - Removed [types] ServiceStatus service status ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~87-~87: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eStatusservice status cache enum. - Removed [utils]cacheInit()` cache initialisat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~88-~88: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t()cache initialisation function. - Removed [utils]cacheReset()` cache reset func...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~89-~89: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...cacheReset()cache reset function. - Removed [utils]cacheSyncBegin()` sync begin c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~90-~90: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...Begin()sync begin cache function. - Removed [utils]cacheSyncEnd()` sync end cache...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~91-~91: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...heSyncEnd()` sync end cache function. - Removed failure email notification functionalit...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~92-~92: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...email notification functionalities. - Removed [types] ChoreoApp Choreo OAuth2 app c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~93-~93: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eo OAuth2 app configuration record. - Removed [types] ErrorEmail error email config...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~94-~94: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...error email configuration record. - Removed [utils]sendErrorEmail()` error notifi...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~120-~120: Ensure spelling is correct
Context: ...le)) - Renamed [types] ConfSalesforce Saleforce configuration record -> `ConfAuthSalesf...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~134-~134: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... annotation to Azure database client. - Added [sf_functions] display annotation to ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~143-~143: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...p:BadRequest->BadRequestError. - Replaced http:InternalServerError->Internal...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~148-~148: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...Detailfor capturing service errors. - Added [types]BadRequestError` for returning...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~149-~149: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eturning bad request (400) responses. - Added [types] InternalServerError for retur...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~150-~150: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nternal server error (500) responses. - Added [types] SyncInfoResponse for returnin...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~182-~182: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eryResult()-> newquery()` method. - Updated [sf_functions] individual Salesforce st...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~183-~183: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... record type configurable sfConfig. - Updated [db_functions] individual database stri...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~184-~184: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ecord type configurable azureMySql. - Updated [records] ConfErrorEmail record field...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~185-~185: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...all capital snake-case to camel-case. - Updated [util] error email configuration name f...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~186-~186: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...e from ERR_EMAIL -> errEmailConf. - Updated [service] service port configuration na...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~187-~187: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...from SERVICE_PORT -> servicePort. - Updated [service] running mode check configurat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~188-~188: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tion name from IS_PROD -> isProd. - Updated [util] sync status check function name ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~189-~189: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...SyncStatus()->checkSyncStatus(). - Updated [util] cacheSyncEnd()` function to ret...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~194-~194: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...o capture Salesforce response errors. - Added [records] ConfDatabase configuration ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~195-~195: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ration record for Azure MySQL client. - Added [sync_config] sync interval period conf...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~202-~202: Ensure spelling is correct
Context: ...lean configurable to dynamically handle enviroment based varibales/logic. ## Version: 1.0.4...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~202-~202: Ensure spelling is correct
Context: ... to dynamically handle enviroment based varibales/logic. ## Version: 1.0.4 ### New - Adde...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~212-~212: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...any active syncs are being processed. - Added 400:Bad Request error response in `/{...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~213-~213: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...viding unsupported sync object types. - Added [ackCaller()] util function to ackowled...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~213-~213: Ensure spelling is correct
Context: ... - Added [ackCaller()] util function to ackowledge the service caller. _(added to avoid ME...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~222-~222: Ensure spelling is correct
Context: ... removed from the cache and dynamically fetcehd from the database. - Updated [cacheInit...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~224-~224: Use a hyphen to join words.
Context: ...tatus()] util function by using database fetched sync times for comparison, inste...

(QB_NEW_EN_HYPHEN)


[style] ~225-~225: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...sing (now removed) cached sync times. - Updated [cacheSyncStatus()] util function by re...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~226-~226: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...(now removed) cached sync start time. - Updated [cacheSyncEnd()] util function by remov...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~227-~227: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ow removed) cached sync success time. - Updated [cacheSyncEnd()] util function by remov...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~276-~276: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...o insert/update Salesforce sync data. - Added database functions to maintain sync sta...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~277-~277: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...(processing, completed, failed) logs. - Added record types for each sync object and e...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~278-~278: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...service/database maintained statuses. - Added caching functions to cache/queue concur...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~279-~279: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...to cache/queue concurrent sync calls. - Added caching functions to maintain sync stat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🔇 Additional comments (14)
salesforce-sync/.gitignore (1)

1-4: Appropriate ignore patterns for Ballerina projects.

The configuration and build output patterns are standard and correct.

salesforce-sync/Ballerina.toml (1)

1-8: Package metadata is properly configured.

Version alignment with changelog and appropriate observability settings for a production service.

salesforce-sync/database/sync_table_update_v1.0.5.sql (1)

1-5: Migration follows standard pattern for schema evolution.

The TRUNCATE-then-ALTER approach is appropriate for adding columns to the Opportunity sync table. Column definition (DECIMAL(16,2)) aligns with changelog.

salesforce-sync/Dependencies.toml (1)

1-4: Auto-generated dependencies manifest is properly marked.

This file should not be hand-edited as noted in the header. The dependency graph includes all necessary packages for Salesforce sync, MySQL operations, and observability.

salesforce-sync/database/sync_table_update_v1.0.6.sql (2)

1-1: Verify migration date accuracy.

The comment shows "29/01/2024" but the PR is dated November 14, 2025. If this is a historical migration being included in the codebase migration, the date is acceptable. However, if this migration was recently created, the year should be updated to 2025 for consistency with the codebase timeline.

Confirm whether this migration is from the historical codebase (Jan 2024) or needs its date updated to reflect the current migration effort (2025).


3-5: Migration follows standard pattern for schema evolution.

The TRUNCATE-then-ALTER approach is appropriate for adding columns to the Account sync table. Column definition (varchar(100)) aligns with existing Account schema patterns.

salesforce-sync/database/sync_table_update_v1.0.1.sql (1)

14-22: Risky column type conversions with existing data.

The CHANGE COLUMN operations (lines 14–22) alter existing ARR columns from their prior type/constraints to DECIMAL(16,2) NOT NULL DEFAULT 0.00. If these columns already contain data, this could fail or truncate values. Ensure compatibility by:

  1. Testing against a production data snapshot first.
  2. Adding a pre-check or validation step to confirm no data loss.
  3. If any columns contain NULL values, populate them before adding NOT NULL constraint.

Please verify these columns currently exist and contain no incompatible data types or NULL values that would fail this migration.

salesforce-sync/database/sync_table_update_v1.0.12.sql (1)

1-11: Second TRUNCATE on sf_opportunity – align with sync behavior and type expectations

As with v1.0.10, this migration is structurally sound but:

  • TRUNCATE sf_opportunity again assumes the table is purely a cache of Salesforce data. Please confirm that the deployment/runbook explicitly expects full repopulation and that no downstream consumer depends on historical rows surviving this migration.
  • The new columns (Subs_*, ARR_Cloud_ARR__c, IAM_ARR_AND_Cloud__c, APIM_ARR_Cloud__c, Integration_ARR_AND_Cloud__c, Direct_Channel__c, Forecast_Type1__c) match the fields in SFOpportunitySyncRecord and in the OPPORTUNITY SOQL, which is good. Just ensure DB nullability (NULL DEFAULT 0.00 for decimals) aligns with how the app treats these fields (nullable vs required).
salesforce-sync/sync_config.bal (1)

8-10: Config defaults look reasonable

accountSync and opportunitySync defaults (enabled with 4-hour period) are a sensible starting point and match the intent of periodic sync. No issues here.

salesforce-sync/types.bal (4)

10-43: Enums and sync config record types look consistent

SyncObject, LogStatus, SfSyncConf, and SfSyncMinimalConf are well-structured and match their usage in the rest of the codebase. No issues here.


50-89: Auth and DB config models are straightforward – just ensure secrets handling practices

SalesforceAuthConf and DatabaseConf are simple and clear. From a code perspective this is fine; just make sure in deployment that:

  • refreshToken, clientId, clientSecret, and DB credentials are provided via secure config sources (env/secret storage) and never logged.

92-121: Account sync record matches the Account SOQL shape

SFAccountSyncRecord fields (including Owner { Email, Name }) line up with the Account SOQL in getSyncConfig, so cloneWithType should be happy as long as Salesforce actually returns those fields. Looks good.


234-239: RecordType wrapper type is aligned with SOQL usage

SFRecordType with nullable Name matches RecordType.Name in the OPPORTUNITY query, and using SFRecordType? RecordType; in SFOpportunitySyncRecord is a good fit. No changes needed.

salesforce-sync/sf_functions.bal (1)

12-33: Salesforce client configuration looks good

The salesforce:Client initialization with configurable base URL/auth, sensible timeout, retry on 500/503, and disabled HTTP/1.1 keepalive is a solid setup for a cron-style sync service. No changes needed here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
salesforce-sync/CHANGELOG.md (1)

202-202: Add hyphen to compound adjective: environment-based.

Line 202 should use a hyphenated compound adjective to follow standard English grammar conventions.

- Added `IS_PROD` boolean configurable to dynamically handle environment based variables/logic.
+ Added `IS_PROD` boolean configurable to dynamically handle environment-based variables/logic.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a7dc21 and 3707f8c.

📒 Files selected for processing (1)
  • salesforce-sync/CHANGELOG.md (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
🪛 LanguageTool
salesforce-sync/CHANGELOG.md

[style] ~68-~68: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ration record name -> DatabaseConf. - Renamed [types] ConfChoreoApp configuration r...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~69-~69: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...iguration record name -> ChoreoApp. - Renamed [types] ConfErrorEmail configuration ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~76-~76: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t to fetch sync status information. - Removed [types] SyncInfo sync info record. ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~77-~77: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...types] SyncInfo sync info record. - Removed [types] SyncInfoItem sync info item r...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~78-~78: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...yncInfoItemsync info item record. - Removed [types]SyncInfoResponse` info respons...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~79-~79: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...InfoResponseinfo response record. - Removed [types]ServiceErrorDetail` service er...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~80-~80: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tailservice error details record. - Removed [types]BadRequestError 400: Bad Req...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~81-~81: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...400: Bad Request response record. - Removed [types] InternalServerError `500: Int...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~82-~82: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...rnal Server Errorresponse record. - Removed [utils]ackCaller()` HTTP caller ackno...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~83-~83: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...HTTP caller acknowledge function. - Removed [utils]generateInfoPayload()` info pa...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~84-~84: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...()` info payload generation function. - Removed the redundant caching functionalities w...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~85-~85: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... functionalities with the new flow. - Removed [types] CacheKeys cache key enum. -...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~86-~86: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...[types] CacheKeys cache key enum. - Removed [types] ServiceStatus service status ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~87-~87: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eStatusservice status cache enum. - Removed [utils]cacheInit()` cache initialisat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~88-~88: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...t()cache initialisation function. - Removed [utils]cacheReset()` cache reset func...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~89-~89: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...cacheReset()cache reset function. - Removed [utils]cacheSyncBegin()` sync begin c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~90-~90: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...Begin()sync begin cache function. - Removed [utils]cacheSyncEnd()` sync end cache...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~91-~91: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...heSyncEnd()` sync end cache function. - Removed failure email notification functionalit...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~92-~92: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...email notification functionalities. - Removed [types] ChoreoApp Choreo OAuth2 app c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~93-~93: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eo OAuth2 app configuration record. - Removed [types] ErrorEmail error email config...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~94-~94: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...error email configuration record. - Removed [utils]sendErrorEmail()` error notifi...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~134-~134: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... annotation to Azure database client. - Added [sf_functions] display annotation to ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~143-~143: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...p:BadRequest->BadRequestError. - Replaced http:InternalServerError->Internal...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~148-~148: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...Detailfor capturing service errors. - Added [types]BadRequestError` for returning...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~149-~149: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eturning bad request (400) responses. - Added [types] InternalServerError for retur...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~150-~150: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nternal server error (500) responses. - Added [types] SyncInfoResponse for returnin...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~182-~182: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...eryResult()-> newquery()` method. - Updated [sf_functions] individual Salesforce st...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~183-~183: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... record type configurable sfConfig. - Updated [db_functions] individual database stri...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~184-~184: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ecord type configurable azureMySql. - Updated [records] ConfErrorEmail record field...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~185-~185: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...all capital snake-case to camel-case. - Updated [util] error email configuration name f...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~186-~186: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...e from ERR_EMAIL -> errEmailConf. - Updated [service] service port configuration na...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~187-~187: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...from SERVICE_PORT -> servicePort. - Updated [service] running mode check configurat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~188-~188: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tion name from IS_PROD -> isProd. - Updated [util] sync status check function name ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~189-~189: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...SyncStatus()->checkSyncStatus(). - Updated [util] cacheSyncEnd()` function to ret...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~194-~194: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...o capture Salesforce response errors. - Added [records] ConfDatabase configuration ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~195-~195: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ration record for Azure MySQL client. - Added [sync_config] sync interval period conf...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~202-~202: Use a hyphen to join words.
Context: ...urable to dynamically handle environment based variables/logic. ## Version: 1.0.4...

(QB_NEW_EN_HYPHEN)


[style] ~212-~212: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...any active syncs are being processed. - Added 400:Bad Request error response in `/{...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~213-~213: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...viding unsupported sync object types. - Added [ackCaller()] util function to acknowle...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~225-~225: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...sing (now removed) cached sync times. - Updated [cacheSyncStatus()] util function by re...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~226-~226: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...(now removed) cached sync start time. - Updated [cacheSyncEnd()] util function by remov...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~227-~227: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ow removed) cached sync success time. - Updated [cacheSyncEnd()] util function by remov...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~276-~276: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...o insert/update Salesforce sync data. - Added database functions to maintain sync sta...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~277-~277: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...(processing, completed, failed) logs. - Added record types for each sync object and e...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~278-~278: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...service/database maintained statuses. - Added caching functions to cache/queue concur...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~279-~279: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...to cache/queue concurrent sync calls. - Added caching functions to maintain sync stat...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
salesforce-sync/main.bal (1)

66-89: Parallel instance guard is best-effort only (race window remains)

executeSync() checks dbCheckProcessing() before inserting the new PROCESSING log. In a multi-instance deployment, two instances can still pass the check concurrently and both insert PROCESSING logs, so this does not fully prevent overlapping runs across nodes.

If you want stronger guarantees, consider making the “no-active-sync” check atomic with log creation (e.g., single-row lock, INSERT ... WHERE NOT EXISTS, or a DB-side mutex pattern) so only one instance can transition from “no active log” to “PROCESSING” at a time.

salesforce-sync/sf_functions.bal (2)

47-61: Remove redundant check vs is error handling around salesforceEP->query

In sfQueryAccount, you both:

  • use check salesforceEP->query(...), which will propagate any error, and
  • declare resultStream as stream<...>|error and test if resultStream is error.

Because of check, resultStream will never be an error at runtime, so the handleSalesforceError path is effectively dead code.

To keep the centralized error mapping via handleSalesforceError, you can drop the check and rely solely on the explicit error branch:

-function sfQueryAccount(SfSyncConf syncConfig) returns SFAccountSyncRecord[]|error {
+function sfQueryAccount(SfSyncConf syncConfig) returns SFAccountSyncRecord[]|error {
     log:printDebug(string `Querying ${ACCOUNT} object on Salesforce...`);
-    stream<SFAccountSyncRecord, error?>|error resultStream = check salesforceEP->query(syncConfig.soqlQuery);
-    if resultStream is error {
-        error err = resultStream;
-        return handleSalesforceError(err);
-    }
+    stream<SFAccountSyncRecord, error?>|error resultStream =
+        salesforceEP->query(syncConfig.soqlQuery);
+    if resultStream is error {
+        return handleSalesforceError(resultStream);
+    }
 
-    SFAccountSyncRecord[] sfRecords =
-        check from var result in resultStream
-        select check result.cloneWithType();
+    SFAccountSyncRecord[] sfRecords = check from var result in resultStream
+        select check result.cloneWithType();
 
     log:printDebug(string `SUCCESS: Received ${sfRecords.length()} records from Salesforce.`);
     return sfRecords;
 }

Same pattern applies to sfQueryOpportunity.


67-81: Mirror the same streamlined pattern for opportunity queries

Once you update sfQueryAccount, please mirror the same error-handling and streaming pattern in sfQueryOpportunity for consistency (no double-handling of error, same handleSalesforceError usage).

salesforce-sync/db_functions.bal (1)

203-217: Minor: error message in dbPrepareInitQuery references the wrong function

The fallback error message mentions [dbPrepareBatchQuery()] even though it’s emitted from dbPrepareInitQuery. Not a functional issue, but can be confusing when debugging.

Consider updating the text to reference dbPrepareInitQuery() instead.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3707f8c and aac04da.

📒 Files selected for processing (6)
  • salesforce-sync/db_functions.bal (1 hunks)
  • salesforce-sync/main.bal (1 hunks)
  • salesforce-sync/sf_functions.bal (1 hunks)
  • salesforce-sync/sync_config.bal (1 hunks)
  • salesforce-sync/types.bal (1 hunks)
  • salesforce-sync/utils.bal (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • salesforce-sync/sync_config.bal
  • salesforce-sync/utils.bal
  • salesforce-sync/types.bal
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
📚 Learning: 2025-11-14T05:14:35.783Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-sync/main.bal
  • salesforce-sync/sf_functions.bal
  • salesforce-sync/db_functions.bal
📚 Learning: 2025-11-14T05:08:35.210Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.

Applied to files:

  • salesforce-sync/main.bal
  • salesforce-sync/db_functions.bal
🔇 Additional comments (7)
salesforce-sync/main.bal (1)

30-61: Overall sync orchestration in main() looks solid

Sequential per-object processing with config gating, up-to-date checks, and a single dbShadowCopy() call guarded by isSyncExecuted is straightforward and matches the intended sync semantics. The do/on fail block provides a clear central error log path without leaking errors to the runtime.

salesforce-sync/sf_functions.bal (1)

20-41: Salesforce client configuration is clear and idiomatic

Top-level salesforce:Client configuration (base URL, auth, timeout, retry on 500/503, HTTP/1.1) is straightforward and should be easy to operate/debug.

salesforce-sync/db_functions.bal (5)

31-42: DB client configuration looks appropriate for Azure MySQL

dbClient setup with explicit host/user/password, UTC serverTimezone, and SSL_PREFERRED is sane for a cloud MySQL deployment and matches typical connector usage.


48-69: Batch sync logic in dbUpdateSync is consistent

The batch-count calculation and int:range(0, batchCount, 1) loop work together to produce contiguous, non-overlapping slices, and dbPrepareInitQuery/dbPrepareBatchQuery separation keeps the SQL generation clear.


80-123: Sync log insert/update flow is clear and robust

dbInsertSyncLog cleanly separates PROCESSING vs COMPLETED/FAILED behaviour, validates logIndex for end logs, and returns the insert ID for the start log, which is then reused for completion/failure. This matches how executeSync in main.bal uses it.


162-191: dbGetLastSyncLog status guard and result handling look good

Restricting to [COMPLETED, FAILED] and explicitly handling sql:NoRowsError vs a real DBSyncLogTimes row vs other error types makes the behaviour of “no matching log vs error” very clear to the caller.


224-444: Account/opportunity batch query builders are consistent with the schema

The dbPrepareBatchQuery implementations for SFAccountSyncRecord[] and SFOpportunitySyncRecord[] produce parameterized INSERT ... ON DUPLICATE KEY UPDATE statements that set IsInSF = 1 on upsert, which aligns with the initial IsInSF = 0 reset and the “mirror cache” semantics from the migrations.

@saajidAha saajidAha changed the title [Common Tools] [Salesforce-Sync] Migrate Salesforce sync codebase to the Common Tools repository [Common Tools] [Salesforce-Sync] Implement Salesforce Sync Service in the Common Tools Repository Nov 14, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
salesforce-sync/types.bal (3)

37-50: Consider adding validation for periodInHours.

The periodInHours field in both SfSyncConf and SfSyncMinimalConf uses the decimal type without constraints. Consider validating that this value is positive when the configuration is loaded to prevent invalid sync intervals.


122-125: Consider extracting the Owner record type to reduce duplication.

The nested Owner record structure (with Email and Name fields) is duplicated between SFAccountSyncRecord (lines 122-125) and SFOpportunitySyncRecord (lines 233-236). Consider extracting this into a shared type:

+# Salesforce owner details
+#
+# + Email - Owner email address  
+# + Name - Owner name
+type SFOwner record {
+    string Email;
+    string Name;
+};
+
 # [Salesforce] Account sync query record.
 # 
 # + Id - Account ID
 # + Name - Account name
 ...
 # + Owner - Account owner details
 type SFAccountSyncRecord record {
     string Id;
     string Name;
     ...
-    record {
-        string Email;
-        string Name;
-    } Owner;
+    SFOwner Owner;
 };

Apply the same change to SFOpportunitySyncRecord.

Also applies to: 233-236


182-238: Large record type may affect maintainability.

SFOpportunitySyncRecord contains approximately 55 fields spanning over 100 lines. While this comprehensively maps the Salesforce Opportunity object, consider whether all fields are actively used. If subsets of fields are accessed in different contexts, you might benefit from creating focused projections or subsets of this type for specific use cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac04da and 5c85664.

📒 Files selected for processing (3)
  • salesforce-sync/database/sync_table_update_v1.0.4.sql (1 hunks)
  • salesforce-sync/database/sync_table_update_v1.0.5.sql (1 hunks)
  • salesforce-sync/types.bal (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • salesforce-sync/database/sync_table_update_v1.0.4.sql
  • salesforce-sync/database/sync_table_update_v1.0.5.sql
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
📚 Learning: 2025-11-14T05:14:35.783Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-sync/types.bal
🔇 Additional comments (3)
salesforce-sync/types.bal (3)

19-29: LGTM! Well-defined enums.

The enum definitions are clear and appropriately scoped for the sync service. Using string values for LogStatus is a good design choice for database persistence.


78-96: LGTM! Clean utility types.

The error and logging types are well-defined with appropriate field types, particularly the use of time:Utc for timestamps.


58-76: No issues found; secure handling is properly implemented.

Verification confirms that sensitive credentials in both SalesforceAuthConf and DatabaseConf are handled securely throughout the codebase:

  • Not logged in plaintext: All log statements (lines in utils.bal, sf_functions.bal, db_functions.bal, main.bal) only log non-sensitive context (object names, counts, timestamps). Sensitive fields never appear in logs.
  • Loaded from secure sources: Both configs use Ballerina's configurable pattern with external bindings (environment variables or config files), not hardcoded values.
  • Not exposed in error messages: Error handling passes only error messages and stack traces to logging—credential fields are never included.

Credentials are passed only to official client libraries (salesforce:Client, mysql:Client), which handle them securely internally.

@suhand
Copy link
Contributor

suhand commented Nov 14, 2025

Let's rename the base directory to salesforce-to-mysql-data-sync

saajidAha and others added 2 commits November 14, 2025 12:27
Co-authored-by: Suhan Dharmasuriya <suhand@users.noreply.github.com>
Co-authored-by: Suhan Dharmasuriya <suhand@users.noreply.github.com>
Co-authored-by: Suhan Dharmasuriya <suhand@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (1)
salesforce-sync/main.bal (1)

24-24: Remove the owner comment per previous review feedback.

The owner line should be removed as requested in the previous review.

Apply this diff:

 # Sync Salesforce objects to a managed database.
 # 
-# Owner: YUK7HI
 # Version: 1.0.0
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c85664 and e342f75.

📒 Files selected for processing (1)
  • salesforce-sync/main.bal (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
📚 Learning: 2025-11-14T05:14:35.783Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.783Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-sync/main.bal
📚 Learning: 2025-11-14T05:08:35.210Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.210Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.

Applied to files:

  • salesforce-sync/main.bal

Co-authored-by: Suhan Dharmasuriya <suhand@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
salesforce-to-mysql-data-sync/db_functions.bal (1)

132-133: Fix time:Utc construction (type mismatch).

As previously noted, line 132 constructs hourAgoUtc as a single-element tuple [int], but time:Utc is defined as readonly & [int, decimal] (a 2-element tuple). This will fail type checking.

Use time:utcAddSeconds to properly compute the time one hour ago:

-    time:Utc hourAgoUtc = [time:utcNow()[0] - (60 * 60)];
-    time:Civil hourAgoTime = time:utcToCivil(hourAgoUtc);
+    time:Utc now = time:utcNow();
+    time:Utc hourAgoUtc = time:utcAddSeconds(now, -3600.0);
+    time:Civil hourAgoTime = time:utcToCivil(hourAgoUtc);
🧹 Nitpick comments (2)
salesforce-to-mysql-data-sync/db_functions.bal (2)

38-40: Consider using stricter SSL mode for production environments.

The MySQL client uses SSL_PREFERRED, which will use SSL if available but falls back to unencrypted connections. For production Azure MySQL deployments, consider using mysql:SSL_REQUIRED to enforce encrypted connections and prevent fallback to unencrypted traffic.

Apply this diff if stricter SSL enforcement is desired:

     options = {
         serverTimezone: "UTC",
         ssl: {
-            mode: mysql:SSL_PREFERRED
+            mode: mysql:SSL_REQUIRED
         }
     }

51-51: Consider tracking the transaction optimization task.

The TODO comment indicates that transactions should be enabled once database connectivity is optimized. This could impact data consistency if the process fails mid-batch.

Would you like me to open a new issue to track this optimization task and ensure it's addressed in a future iteration?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fa832c and 5bd8063.

📒 Files selected for processing (3)
  • salesforce-to-mysql-data-sync/Ballerina.toml (1 hunks)
  • salesforce-to-mysql-data-sync/Dependencies.toml (1 hunks)
  • salesforce-to-mysql-data-sync/db_functions.bal (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
📚 Learning: 2025-11-14T05:14:35.791Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-to-mysql-data-sync/db_functions.bal
📚 Learning: 2025-11-14T05:08:35.231Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.

Applied to files:

  • salesforce-to-mysql-data-sync/db_functions.bal
🔇 Additional comments (5)
salesforce-to-mysql-data-sync/db_functions.bal (4)

79-124: LGTM!

The function correctly handles all log status cases with appropriate validation. The check for invalid logIndex (line 105-107) prevents updates with an uninitialized index, and the match expression comprehensively covers all status types.


161-190: LGTM!

The function correctly validates that only completed or failed logs can be retrieved (lines 163-165) and handles all result cases appropriately, including the no-rows scenario.


195-214: LGTM!

Both functions are well-implemented. dbShadowCopy properly closes the procedure result (line 198), and dbPrepareInitQuery uses type-safe dispatch with clear error handling for unsupported record types.


220-440: LGTM!

The batch query generation is well-implemented with proper parameterization to prevent SQL injection. The use of ON DUPLICATE KEY UPDATE (lines 252-266, 381-435) ensures idempotent upsert behavior, and the IsInSF = 1 flag correctly marks synced records. Optional chaining for RecordType?.Name (lines 336, 391) appropriately handles nullable fields.

salesforce-to-mysql-data-sync/Ballerina.toml (1)

5-5: No update required—version is current.

Verification confirms that 2201.13.0 matches the latest available Ballerina release. The project is already on the most current version and does not need updating.

@saajidAha saajidAha requested review from suhand and yuk7hi November 14, 2025 09:42
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
salesforce-to-mysql-data-sync/db_functions.bal (1)

125-149: CRITICAL: time:Utc construction is still incorrect despite past review.

Line 127 creates a single-element tuple [time:utcNow()[0] - (60 * 60)], but time:Utc requires a two-element tuple [int, decimal] representing seconds and fractional seconds from the epoch. This will fail type checking.

The past review comment indicated this was addressed in commits 257c47c to 5bd8063, but the incorrect code is still present.

Apply this fix:

-    time:Utc hourAgoUtc = [time:utcNow()[0] - (60 * 60)];
+    time:Utc hourAgoUtc = time:utcAddSeconds(time:utcNow(), -3600.0);
🧹 Nitpick comments (1)
salesforce-to-mysql-data-sync/sf_functions.bal (1)

43-77: Consider extracting common query pattern.

Both sfQueryAccount and sfQueryOpportunity follow identical logic (debug log → query → error handling → stream-to-array → success log). You could extract a generic helper:

function sfQueryGeneric<T>(string objectName, string soqlQuery) returns T[]|error {
    log:printDebug(string `Querying ${objectName} object on Salesforce...`);
    stream<T, error?>|error resultStream = check salesforceEP->query(soqlQuery);
    if resultStream is error {
        return handleSalesforceError(resultStream);
    }
    T[] sfRecords = check from var result in resultStream select check result.cloneWithType();
    log:printDebug(string `SUCCESS: Received ${sfRecords.length()} records from Salesforce.`);
    return sfRecords;
}

However, the current approach provides stronger type safety at compile time, so this refactor is optional.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bd8063 and a50d37a.

📒 Files selected for processing (5)
  • salesforce-to-mysql-data-sync/database/sync_table_creation.sql (1 hunks)
  • salesforce-to-mysql-data-sync/db_functions.bal (1 hunks)
  • salesforce-to-mysql-data-sync/sf_functions.bal (1 hunks)
  • salesforce-to-mysql-data-sync/sync_config.bal (1 hunks)
  • salesforce-to-mysql-data-sync/types.bal (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • salesforce-to-mysql-data-sync/database/sync_table_creation.sql
  • salesforce-to-mysql-data-sync/sync_config.bal
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
📚 Learning: 2025-11-14T05:14:35.791Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-to-mysql-data-sync/sf_functions.bal
  • salesforce-to-mysql-data-sync/db_functions.bal
📚 Learning: 2025-11-14T05:08:35.231Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.

Applied to files:

  • salesforce-to-mysql-data-sync/db_functions.bal
🔇 Additional comments (13)
salesforce-to-mysql-data-sync/sf_functions.bal (2)

20-21: LGTM! Configurable variables properly defined.

The required configurable pattern ensures Salesforce credentials and base URL must be provided at deployment, preventing runtime failures from missing configuration.


28-37: LGTM! Reasonable timeout and retry configuration.

The 300-second timeout accommodates large SOQL queries, and the retry policy (3 attempts, 5-second interval) on 500/503 errors handles transient failures appropriately. Since SOQL queries are idempotent read operations, retries are safe.

salesforce-to-mysql-data-sync/db_functions.bal (6)

22-42: LGTM! Database configuration and client initialization are well-structured.

The MySQL client configuration correctly sets UTC timezone to avoid DST issues and enables SSL with SSL_PREFERRED mode for secure connections when available.


48-64: LGTM! Transaction ensures atomic sync operations.

The transaction wraps both the initialization query (setting IsInSF = 0) and batch inserts/updates, ensuring atomicity. The pattern of resetting IsInSF before sync allows detection of records removed from Salesforce. Error handling properly logs and returns errors while rolling back on failure.


74-119: LGTM! Log insertion/update logic is well-structured.

The function correctly handles different log statuses:

  • PROCESSING: Inserts a new log entry and returns the insert ID
  • COMPLETED/FAILED: Updates the existing log entry using logIndex

The validation at line 100-102 properly prevents invalid updates when logIndex = -1.


156-185: LGTM! Last sync log retrieval is properly implemented.

The function correctly:

  • Validates that only COMPLETED or FAILED statuses can be queried (lines 158-160)
  • Uses ORDER BY id DESC LIMIT 1 to fetch the most recent log
  • Handles the NoRowsError case by returning nil
  • Provides type-safe results via DBSyncLogTimes

190-194: LGTM! Shadow copy procedure call is correctly implemented.

The function properly calls the arr_shadow_copy() stored procedure and closes the result handle to prevent resource leaks.


200-279: LGTM! Query generation functions handle both record types correctly.

The functions properly:

  • Use type-based dispatch to generate appropriate SQL for Account and Opportunity records
  • Implement upsert behavior via INSERT ... ON DUPLICATE KEY UPDATE
  • Set IsInSF = 1 during insert/update to mark records present in Salesforce
  • Return descriptive errors for unsupported record types

The pattern aligns with the sync strategy where IsInSF flags indicate which records are currently in Salesforce.

Based on learnings

salesforce-to-mysql-data-sync/types.bal (5)

18-29: LGTM! Enum definitions are clear and appropriate.

The SyncObject and LogStatus enums provide type-safe constants for sync operations and log states.


31-47: LGTM! Sync configuration records are well-structured.

The distinction between SfSyncConf (with explicit SOQL query) and SfSyncMinimalConf (without query) provides flexibility for different configuration patterns. Using decimal for periodInHours allows fractional hour intervals.


49-71: LGTM! Configuration records properly structured for sensitive credentials.

Both SalesforceAuthConf and DatabaseConf are marked with [Configurable] in their documentation and use closed record syntax. These records are correctly used with Ballerina's configurable keyword in sf_functions.bal and db_functions.bal, ensuring credentials are provided at runtime via configuration files or environment variables rather than hardcoded.


73-89: LGTM! Utility and database records properly defined.

The DBSyncLogTimes record correctly uses time:Utc type for timestamp fields, enabling proper time arithmetic and timezone handling.


91-129: LGTM! Salesforce sync records are appropriately defined.

The record definitions are clean and straightforward:

  • Optional fields (BillingCountry?, ShippingCountry?) correctly use the ? marker
  • Inline Owner nested records provide clear structure
  • Field types align with Salesforce data types

The past review comments about missing optional markers on ARR and boolean fields no longer apply, as those fields have been removed from these simplified record definitions.

@saajidAha saajidAha requested a review from yuk7hi November 20, 2025 05:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
salesforce-to-mysql-data-sync/db_functions.bal (1)

127-128: The time:Utc construction is still incorrect.

Line 127 creates a single-element tuple, but time:Utc is defined as readonly & [int, decimal] (a 2-element tuple containing both seconds and fractional seconds). This will fail type checking.

Although a previous review comment indicated this was addressed, the current code still shows the problematic construction.

Apply this diff to fix the time calculation:

-    time:Utc hourAgoUtc = [time:utcNow()[0] - (60 * 60)];
-    time:Civil hourAgoTime = time:utcToCivil(hourAgoUtc);
+    time:Utc now = time:utcNow();
+    time:Utc hourAgoUtc = time:utcAddSeconds(now, -3600.0);
+    time:Civil hourAgoTime = time:utcToCivil(hourAgoUtc);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a50d37a and da1f271.

📒 Files selected for processing (2)
  • salesforce-to-mysql-data-sync/.gitignore (1 hunks)
  • salesforce-to-mysql-data-sync/db_functions.bal (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • salesforce-to-mysql-data-sync/.gitignore
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.
📚 Learning: 2025-11-14T05:14:35.791Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_update_v1.0.14.sql:1-4
Timestamp: 2025-11-14T05:14:35.791Z
Learning: In the salesforce-sync project, TRUNCATE statements in migration files (e.g., salesforce-sync/database/sync_table_update_v*.sql) are intentional and follow a common pattern for the sync process. The database tables (sf_opportunity, sf_account) act as a cache/mirror of Salesforce data that can be fully reloaded from the Salesforce API, so data loss from TRUNCATE is not critical.

Applied to files:

  • salesforce-to-mysql-data-sync/db_functions.bal
📚 Learning: 2025-11-14T05:08:35.231Z
Learnt from: saajidAha
Repo: wso2-open-operations/common-tools PR: 1
File: salesforce-sync/database/sync_table_creation.sql:3-5
Timestamp: 2025-11-14T05:08:35.231Z
Learning: The file salesforce-sync/database/sync_table_creation.sql with DROP DATABASE IF EXISTS is intentionally destructive and only used for initial setup in local environments, not for existing running systems.

Applied to files:

  • salesforce-to-mysql-data-sync/db_functions.bal
🔇 Additional comments (5)
salesforce-to-mysql-data-sync/db_functions.bal (5)

44-64: Transaction implementation looks solid.

The function correctly uses a transaction block with proper error handling. All queries are parameterized, which prevents SQL injection vulnerabilities.


66-119: Well-structured log management with proper validation.

The pattern matching on LogStatus is clean, and the validation ensuring logIndex != -1 for update operations prevents invalid updates. Good defensive programming.


151-185: Proper validation and error handling for log retrieval.

The status validation and handling of the NoRowsError case are both correct. The query is properly parameterized and the separate variable assignment improves readability for the multi-line query.


187-194: LGTM!

The stored procedure call is correct and the result is properly closed to prevent resource leaks.


211-279: Excellent use of parameterized queries and query expressions.

The upsert pattern with ON DUPLICATE KEY UPDATE is well-implemented, and the query expressions make the batch generation clean and maintainable. All queries are properly parameterized, preventing SQL injection.

@chanukaranaba chanukaranaba merged commit 63e3783 into wso2-open-operations:main Nov 24, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Salesforce Sync Implementation to the Common Tools (Open Source) Repository

4 participants