Skip to content

Modify file location according to new structure - #129

Open
NadhiyaNashath wants to merge 23 commits into
wso2:mainfrom
NadhiyaNashath:github-salesforce
Open

Modify file location according to new structure#129
NadhiyaNashath wants to merge 23 commits into
wso2:mainfrom
NadhiyaNashath:github-salesforce

Conversation

@NadhiyaNashath

Copy link
Copy Markdown

Purpose

Modify the file location according to the new structure

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Overview

Reorganizes and completes the GitHub Issues to Salesforce integration sample under the new directory structure (integrator-default-profile/samples/github-issue-to-salesforce/). This PR implements a fully functional webhook listener that converts labeled GitHub issues into Salesforce Case records with configurable field mappings and validation logic.

Key Changes

Integration Implementation

  • GitHub Webhook Listener: Implements a github:IssuesService that listens for GitHub issue events (opened, closed, reopened, assigned, unassigned, labeled, unlabeled)
  • Label-based Filtering: The onLabeled handler checks issue labels against configurable trigger labels and only creates Salesforce cases for matching labels
  • Salesforce Case Creation: Maps GitHub issue data (title, body, URL) to Salesforce Case fields with configurable defaults for status, priority, record type, and owner ID
  • Error Handling: Implements detailed error handling for Salesforce API responses, including specific logging for authentication (401) and validation (400) errors

Configuration & Metadata

  • Config Schema (config-schema.json): Defines three main configuration sections:

    • githubConfig: webhook secret and trigger labels
    • salesforceConfig: base URL and OAuth2 credentials (client ID/secret, refresh token)
    • caseConfig: Salesforce case defaults (record type, priority, status, owner ID)
    • ballerina and log sections for HTTP listener and logging configuration
  • Package Configuration (Ballerina.toml): Establishes package metadata (org: wso2, name: github_issue_to_salesforce, version: 0.1.0)

Documentation & Setup

  • README: Provides end-to-end documentation including required Salesforce setup (OAuth2 external client app, custom URL field creation) and deployment steps for the Choreo platform
  • Instructions (instructions.md): Outlines integration workflow, GitHub webhook setup, Salesforce OAuth2 configuration, and custom field creation steps
  • Diagram (diagram.md): Visual workflow showing event filtering by label and repository, then case creation

Project Structure

  • Component Configuration (component.yaml): Defines Choreo component with public REST webhook endpoint on port 9090
  • Type Definitions (types.bal): Adds Label and SalesforceCase record types for type safety
  • Client Initialization (connections.bal): Creates a module-level Salesforce client using configured OAuth2 credentials
  • Projects Registry: Updates .github/workflows/projects.json to register the new sample in the project list

Technical Details

  • Language: Ballerina integration platform
  • Connectors: Uses ballerinax/salesforce and github modules
  • Authentication: OAuth2 refresh token flow for Salesforce API access
  • Field Mapping: GitHub issue URL stored in custom Salesforce field GitHub_Issue_URL__c
  • Build Artifacts: .gitignore configured to exclude Ballerina compiler artifacts and development configuration

Walkthrough

This PR adds a complete GitHub Issues to Salesforce Case integration sample package. The sample includes a Ballerina webhook listener that receives GitHub issue events, filters by configured trigger labels, extracts issue details, and creates corresponding Salesforce Case records with field mapping and a custom GitHub issue URL field. Supporting files include configuration schema, setup instructions, type definitions, client initialization, and comprehensive documentation. Additionally, a reference to a new github-issue-to-google-chat sample is added to the projects manifest.

Sequence Diagram

sequenceDiagram
    actor GitHub
    participant Listener as Ballerina Listener
    participant Service as Issues Service
    participant Salesforce as Salesforce API

    GitHub->>Listener: POST webhook (IssuesEvent)
    Listener->>Service: onLabeled(payload)
    Service->>Service: Extract label name
    Service->>Service: Check if label in triggerLabels
    
    alt Label matches
        Service->>Service: Extract issue details<br/>(title, body, url)
        Service->>Service: Build SalesforceCase<br/>with caseConfig
        Service->>Salesforce: create(Case, sObject)
        
        alt Success
            Salesforce-->>Service: Case ID
            Service-->>Listener: Success
        else Error
            Salesforce-->>Service: Error response<br/>(401/400/other)
            Service->>Service: Log error<br/>with status code
            Service-->>Listener: error("Failed to create...")
        end
    else Label not matched
        Service-->>Listener: No action
    end
    
    Listener-->>GitHub: Response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is minimal, providing only a single line that restates the title without substantial detail. While it addresses the main purpose, it lacks meaningful elaboration on goals, approach, or the scope of changes introduced. Expand the description to include goals and approach sections explaining the new file structure, why this reorganization is necessary, and any breaking changes or migration steps if applicable.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title references modifying file location according to new structure, which directly aligns with the changeset that adds a new entry to projects.json and reorganizes files for the github-issue-to-salesforce sample.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
integrator-default-profile/samples/github-issue-to-salesforce/main.bal (1)

81-84: ⚡ Quick win

Prefer direct typed access over unsafe map<anydata> cast for statusCode.

err.detail() is assigned to map<anydata>, and errorDetail["statusCode"] (which returns anydata, inclusive of () in Ballerina) is then cast with <int>. This cast panics at runtime if the key is absent or the value is not an int. Since err is http:ApplicationResponseError is already established, err.detail() returns a typed record with statusCode: int — access it directly.

♻️ Proposed refactor
- map<anydata> errorDetail = err.detail();
- int statusCode = <int>errorDetail["statusCode"];
+ int statusCode = err.detail().statusCode;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrator-default-profile/samples/github-issue-to-salesforce/main.bal`
around lines 81 - 84, Replace the unsafe map<anydata> cast and index access by
capturing err.detail() into a typed record and read the statusCode field
directly: since you already checked err is http:ApplicationResponseError, assign
the detail to a record variable (e.g. `var detail = err.detail();` or declare a
record type with `statusCode: int` and do `YourDetailType detail =
err.detail();`) and then use `detail.statusCode` to get the int instead of
`<int>errorDetail["statusCode"]`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@integrator-default-profile/samples/github-issue-to-salesforce/.choreo/diagram.md`:
- Line 4: The diagram includes a decision node D ("Is repository in
githubRepositories?") but the configuration lacks a githubRepositories field;
either add githubRepositories to githubConfig (update config.bal and
config-schema.json to declare it and its type) and document it in
instructions.md so the diagram's repository filter is configurable, or remove
node D from diagram.md so it matches the current githubConfig (webhookSecret and
triggerLabels) and instructions; update whichever files you change
(githubConfig, config.bal, config-schema.json, instructions.md, diagram.md) to
keep diagram and config in sync.

In `@integrator-default-profile/samples/github-issue-to-salesforce/README.md`:
- Around line 53-69: The README lists flat configuration keys that don't match
the structured Ballerina config record in config.bal; update the README to
either (1) replace flat keys with the structured TOML keys under the
salesforceConfig and caseConfig records (e.g., salesforceConfig.baseUrl,
clientId, clientSecret, refreshToken, refreshUrl and caseConfig.status,
priority, recordType, ownerId) so local Config.toml users can copy/paste correct
keys, or (2) explicitly state that the flat names are only for the Devant UI and
show both representations (Devant flat names and the corresponding config.bal
record/TOML keys) so readers know which to use when testing locally or via the
UI. Ensure you reference the config.bal records salesforceConfig and caseConfig
in the README text.
- Around line 49-51: Add documentation for the required
githubConfig.webhookSecret under the "GitHub Configurations" section: explain
that webhookSecret is a required string used to validate incoming GitHub
webhooks and must match the secret configured in GitHub, show the config key
name webhookSecret (as referenced in config.bal) and note that main.bal uses it
to initialize the GitHub listener; include a short example value placeholder and
a one-line note about keeping it secret.

In `@integrator-default-profile/samples/github-issue-to-salesforce/types.bal`:
- Line 15: The struct/record field named default is a reserved keyword and must
be escaped; update the field declaration in types.bal from boolean default; to
use the quoted identifier ('default) and then update all references to this
field throughout the module to use 'default (e.g., in constructors, accesses,
JSON mapping, or serialization code) so the code compiles; ensure any pattern
matches or JSON keys that relied on unquoted default are adjusted to the quoted
identifier or mapped appropriately.

---

Nitpick comments:
In `@integrator-default-profile/samples/github-issue-to-salesforce/main.bal`:
- Around line 81-84: Replace the unsafe map<anydata> cast and index access by
capturing err.detail() into a typed record and read the statusCode field
directly: since you already checked err is http:ApplicationResponseError, assign
the detail to a record variable (e.g. `var detail = err.detail();` or declare a
record type with `statusCode: int` and do `YourDetailType detail =
err.detail();`) and then use `detail.statusCode` to get the int instead of
`<int>errorDetail["statusCode"]`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e58682de-4f5f-4c90-b559-e4459f76af2b

📥 Commits

Reviewing files that changed from the base of the PR and between 25b1cdb and abec59e.

📒 Files selected for processing (15)
  • .github/workflows/projects.json
  • integrator-default-profile/samples/github-issue-to-salesforce/.choreo/component.yaml
  • integrator-default-profile/samples/github-issue-to-salesforce/.choreo/config-schema.json
  • integrator-default-profile/samples/github-issue-to-salesforce/.choreo/diagram.md
  • integrator-default-profile/samples/github-issue-to-salesforce/.choreo/instructions.md
  • integrator-default-profile/samples/github-issue-to-salesforce/.gitignore
  • integrator-default-profile/samples/github-issue-to-salesforce/Ballerina.toml
  • integrator-default-profile/samples/github-issue-to-salesforce/README.md
  • integrator-default-profile/samples/github-issue-to-salesforce/agents.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/config.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/connections.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/data_mappings.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/functions.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/main.bal
  • integrator-default-profile/samples/github-issue-to-salesforce/types.bal

A(["Begin"]):::startNode
B["Receive GitHub Webhook Event"]:::processNode
C{"Is label in <br/> triggerLabels?"}:::decisionNode
D{"Is repository in <br/> githubRepositories?"}:::decisionNode

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

githubRepositories referenced in diagram but missing from configuration.

Decision node D ("Is repository in githubRepositories?") implies a repository-based filter, but githubConfig in config.bal and config-schema.json only defines webhookSecret and triggerLabels — there is no githubRepositories field. The instructions.md likewise omits it as a configurable option.

Either add githubRepositories to githubConfig (and the schema) if this filtering is intended, or remove node D from the diagram.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@integrator-default-profile/samples/github-issue-to-salesforce/.choreo/diagram.md`
at line 4, The diagram includes a decision node D ("Is repository in
githubRepositories?") but the configuration lacks a githubRepositories field;
either add githubRepositories to githubConfig (update config.bal and
config-schema.json to declare it and its type) and document it in
instructions.md so the diagram's repository filter is configurable, or remove
node D from diagram.md so it matches the current githubConfig (webhookSecret and
triggerLabels) and instructions; update whichever files you change
(githubConfig, config.bal, config-schema.json, instructions.md, diagram.md) to
keep diagram and config in sync.

Comment on lines +49 to +51
### GitHub Configurations
- `triggerLabels` - List of labels that trigger case creation
(e.g., `["bug", "support"]`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

webhookSecret is missing from the GitHub Configurations section.

githubConfig.webhookSecret (defined in config.bal line 3) is required for the GitHub listener initialization in main.bal line 5, but is not documented. Users will be unable to configure the integration correctly without knowing this value is required.

📝 Suggested addition
 ### GitHub Configurations
+- `webhookSecret` - Secret token used to validate incoming GitHub webhook payloads
 - `triggerLabels` - List of labels that trigger case creation 
   (e.g., `["bug", "support"]`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrator-default-profile/samples/github-issue-to-salesforce/README.md`
around lines 49 - 51, Add documentation for the required
githubConfig.webhookSecret under the "GitHub Configurations" section: explain
that webhookSecret is a required string used to validate incoming GitHub
webhooks and must match the secret configured in GitHub, show the config key
name webhookSecret (as referenced in config.bal) and note that main.bal uses it
to initialize the GitHub listener; include a short example value placeholder and
a one-line note about keeping it secret.

Comment on lines +53 to +69
### Salesforce Credentials
- `salesforceBaseUrl` - Your Salesforce instance URL
(e.g., `https://your-instance.my.salesforce.com`)
- `salesforceClientId` - Your Salesforce Connected App/External Client App Client ID
- `salesforceClientSecret` - Your Salesforce Connected App/External Client App Client Secret
- `salesforceRefreshToken` - Your Salesforce OAuth refresh token
- `salesforceRefreshUrl` - Your Salesforce OAuth token endpoint
(e.g., `https://login.salesforce.com/services/oauth2/token` for production or
`https://test.salesforce.com/services/oauth2/token` for sandbox)

### Salesforce Case Defaults
- `caseStatus` - Default status for created cases (e.g., `"New"`)
- `casePriority` - Default priority for created cases
(e.g., `"Medium"`)
- `caseRecordType` - Default type for created cases
(e.g., `"User"`)
- `caseOwnerId` - Salesforce User ID or Queue ID to assign cases to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Configuration key names do not match the actual Ballerina configurable record structure.

The README uses flat aliases (e.g., salesforceBaseUrl, caseStatus, caseOwnerId) that do not correspond to the actual field names in config.bal. For local Config.toml-based configuration, the keys are structured under record sections. For example:

[salesforceConfig]
baseUrl = "..."
clientId = "..."
clientSecret = "..."
refreshToken = "..."
refreshUrl = "..."

[caseConfig]
status = "New"
priority = "Medium"
recordType = "User"
ownerId = "..."

If the flat names are intentional for the Devant UI configuration screen, clarify this distinction so developers testing locally know the correct TOML structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrator-default-profile/samples/github-issue-to-salesforce/README.md`
around lines 53 - 69, The README lists flat configuration keys that don't match
the structured Ballerina config record in config.bal; update the README to
either (1) replace flat keys with the structured TOML keys under the
salesforceConfig and caseConfig records (e.g., salesforceConfig.baseUrl,
clientId, clientSecret, refreshToken, refreshUrl and caseConfig.status,
priority, recordType, ownerId) so local Config.toml users can copy/paste correct
keys, or (2) explicitly state that the flat names are only for the Devant UI and
show both representations (Devant flat names and the corresponding config.bal
record/TOML keys) so readers know which to use when testing locally or via the
UI. Ensure you reference the config.bal records salesforceConfig and caseConfig
in the README text.

// 6-character hex code, without the leading #, identifying the color
string color;
// Whether this label comes by default in a new repository
boolean default;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all usages of the `default` field on Label values in the codebase
# to confirm they also need updating to 'default
rg -n "\.default\b" --type bal -A2 -B2

Repository: wso2/integration-samples

Length of output: 96


🏁 Script executed:

# First, locate and examine the specific file mentioned in the review
fd -i "types.bal" -x head -20 {}

Repository: wso2/integration-samples

Length of output: 14042


🏁 Script executed:

# Search for .default usages in .bal files without using invalid --type argument
fd -e bal -x grep -n "\.default\b" {} + 2>/dev/null || true

Repository: wso2/integration-samples

Length of output: 50


🌐 Web query:

Ballerina programming language reserved keywords escaping single quote

💡 Result:

In Ballerina, single quotes do not “escape” reserved keywords in general strings. Instead, reserved keywords can be used as identifiers (variable/field names) by using a quoted identifier: prefix the keyword with a single quote, e.g. 'resource, 'function, or 'K. For example, if resource is a reserved keyword, field access uses the quoted identifier form: - j2.'resource This is described in the Ballerina language specification under the QuotedIdentifier syntax (single quote preceding a reserved keyword).

Citations:


🏁 Script executed:

fd "types.bal" -x wc -l {} \;

Repository: wso2/integration-samples

Length of output: 15826


🏁 Script executed:

fd "types.bal" -x cat -n {} \; | grep -A2 -B2 "boolean default"

Repository: wso2/integration-samples

Length of output: 210


🏁 Script executed:

# Check if the file path from review exists
find . -path "*integrator-default-profile/samples/github-issue-to-salesforce/types.bal" -type f

Repository: wso2/integration-samples

Length of output: 142


Field name default must be quoted with single quote prefix — reserved keyword will cause compilation failure.

The field declaration boolean default; uses a reserved keyword without proper escaping. Per the Ballerina language specification, reserved keywords used as identifiers must be preceded by a single quote (e.g., 'default). This code will fail to compile as written.

Fix
-    boolean default;
+    boolean 'default;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
boolean default;
boolean 'default;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrator-default-profile/samples/github-issue-to-salesforce/types.bal` at
line 15, The struct/record field named default is a reserved keyword and must be
escaped; update the field declaration in types.bal from boolean default; to use
the quoted identifier ('default) and then update all references to this field
throughout the module to use 'default (e.g., in constructors, accesses, JSON
mapping, or serialization code) so the code compiles; ensure any pattern matches
or JSON keys that relied on unquoted default are adjusted to the quoted
identifier or mapped appropriately.

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.

1 participant