Integrate the module with ECA

Created on 13 March 2025, 4 months ago

Problem/Motivation

I have a client who uses Salesforce extensively, and we see the potential to create custom automations between their Drupal site and Salesforce CRM using the ECA module.

Currently, several integrations require custom module development for anything beyond basic field mapping, which increases development and maintenance costs.

The ECA (Event, Condition, Action) module provides a powerful no-code solution for workflow automation within Drupal, but it doesn't currently connect with Salesforce events or actions. An integration between these systems would allow site builders to create sophisticated Salesforce integrations through configuration instead of custom code.

Steps to reproduce

Not applicable for a feature request.

Proposed resolution

Create a `salesforce_eca` submodule for the Salesforce Suite that potentially could:

1. Expose Salesforce events to ECA (object creation, field updates, deletions, etc.)
2. Provide ECA conditions for evaluating Salesforce data (field values, record existence)
3. Implement ECA actions for triggering Salesforce operations (create/update records, execute API calls)

This would enable users to build complex integrations between Drupal and Salesforce using ECA's configuration interface rather than writing custom code. Expanding the reach of the salesforce integration.

Remaining tasks

- Assess community interest in this integration
- Document common use cases
- Identify specific events, conditions, and actions to implement in the initial version
- Determine technical approach for integration with Salesforce APIs
- Design plugin architecture for extensibility
- Develop initial implementation

User interface changes

No direct UI changes to existing interfaces. The module would:
- Add new events to the ECA event selection
- Add new conditions related to Salesforce data
- Add new actions for Salesforce operations

All would be integrated into the existing ECA interface.

API changes

This module would leverage existing Salesforce Suite and ECA APIs.

Data model changes

None anticipated for the initial implementation. The module would use the existing data structures of both the Salesforce Suite and ECA modules.

✨ Feature request
Status

Active

Version

5.1

Component

salesforce.module

Created by

πŸ‡ΊπŸ‡ΈUnited States camoa

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @camoa
  • πŸ‡ΊπŸ‡ΈUnited States camoa
  • πŸ‡ΊπŸ‡ΈUnited States camoa
  • πŸ‡©πŸ‡ͺGermany jurgenhaas Gottmadingen

    This sounds amazing. As an ECA maintainer I'd be happy to provide help when needed.

  • πŸ‡ΊπŸ‡ΈUnited States armyguyinfl Florida

    Let's go! Can't wait for the Salesforce integration.

  • πŸ‡ΊπŸ‡ΈUnited States camoa

    After reviewing the Salesforce module, I've put together a list of potential events, conditions, and actions we could implement for the ECA integration. This is based on the existing Salesforce module architecture to ensure we're leveraging what's already available:

    Events

    Push Events

    • salesforce_eca.push_success - Uses SalesforceEvents::PUSH_SUCCESS
    • salesforce_eca.push_fail - Uses SalesforceEvents::PUSH_FAIL
    • salesforce_eca.push_params - Uses SalesforceEvents::PUSH_PARAMS
    • salesforce_eca.push_allowed - Uses SalesforceEvents::PUSH_ALLOWED

    Pull Events

    • salesforce_eca.pull_presave - Uses SalesforceEvents::PULL_PRESAVE
    • salesforce_eca.pull_entity_value - Uses SalesforceEvents::PULL_ENTITY_VALUE
    • salesforce_eca.pull_prepull - Uses SalesforceEvents::PULL_PREPULL
    • salesforce_eca.pull_query - Uses SalesforceEvents::PULL_QUERY

    Error Events

    • salesforce_eca.error - Uses SalesforceEvents::ERROR
    • salesforce_eca.warning - Uses SalesforceEvents::WARNING
    • salesforce_eca.notice - Uses SalesforceEvents::NOTICE

    Conditions

    Object Conditions

    • salesforce_object_type - Uses SObject class type checking
    • salesforce_field_value - Uses SObject field access methods
    • salesforce_object_exists - Uses RestClient::objectRead() or RestClient::objectReadbyExternalId()

    Mapping Conditions

    • salesforce_is_mapped - Uses SalesforceMapping entity queries
    • salesforce_field_is_mapped - Uses SalesforceMappingFieldPluginManager
    • salesforce_mapping_direction - Uses SalesforceMapping properties

    Entity Conditions

    • entity_has_sfid - Uses MappedObject entity queries
    • entity_sync_status - Uses MappedObject status property
    • entity_recently_synced - Uses MappedObject updated timestamp

    System Conditions

    • salesforce_is_connected - Uses RestClient::isInit()
    • salesforce_api_limits - Uses RestClient::getApiUsage()
    • salesforce_queue_status - Uses PushQueue service

    Actions

    Push Actions

    • salesforce_push_entity - Uses MappedObject::push()
    • salesforce_push_multiple - Uses batch operations with MappedObject::push()
    • salesforce_queue_push - Uses PushQueue::createItem()

    Pull Actions

    • salesforce_pull_object - Uses RestClient::objectRead() and pull system
    • salesforce_pull_by_query - Uses SelectQuery objects with RestClient::query()
    • salesforce_queue_pull - Uses PullQueue::createItem()

    API Actions

    • salesforce_execute_soql - Uses SelectQuery objects with RestClient::query()
    • salesforce_api_call - Uses RestClient::apiCall()
    • salesforce_create_object - Uses RestClient::objectCreate()
    • salesforce_update_object - Uses RestClient::objectUpdate()
    • salesforce_delete_object - Uses RestClient::objectDelete()
    • salesforce_upsert_object - Uses RestClient::objectUpsert()

    Mapping Actions

    • salesforce_create_mapping - Uses SalesforceMapping entity creation
    • salesforce_update_mapping - Uses SalesforceMapping entity update
    • salesforce_delete_mapping - Uses SalesforceMapping entity deletion
    • salesforce_sync_mapping - Uses SalesforceMapping with batch operations

    Data Transformation Actions

    • salesforce_transform_data - Would need custom transformations, potentially with ECA's data plugins
    • salesforce_export_to_variable - Uses ECA variable system with Salesforce data

    Utility Actions

    • salesforce_log - Uses Drupal logger system
    • salesforce_clear_cache - Uses Cache tags and bins related to Salesforce
    • salesforce_connection_test - Uses RestClient::apiCall() with HEAD method

    Common Use Cases

    This integration would enable several key use cases without custom code:

    1. Enhanced Lead Generation: Trigger custom email sequences when leads are created in Salesforce
    2. Conditional Synchronization: Only push content to Salesforce if it meets specific criteria
    3. Cross-Platform Workflows: Create workflows spanning both systems
    4. Data Enrichment: Pull additional data from Salesforce based on form submissions
    5. Reporting Automation: Generate reports when Salesforce data meets thresholds
    6. Data Transformation Pipeline: Transform data formats between systems
    7. Batch Processing Management: Schedule and manage large-scale synchronization
    8. Connection Monitoring: Automatically monitor Salesforce connectivity

    This is just a starting point - if there are other actions, events, or conditions people would find useful, let me know and we can expand this list!

  • πŸ‡ΊπŸ‡ΈUnited States camoa

    Initial Implementation of the Salesforce ECA Module and Execute SOQL Query Action

    This comment provides an overview of the initial implementation of the Salesforce ECA module, focusing on the first action we've created: salesforce_execute_soql.

    Module Structure

    We set up the following directory structure for the salesforce_eca module:

    salesforce_eca/
    β”œβ”€β”€ config/
    β”‚   β”œβ”€β”€ install/
    β”‚   β”‚   └── system.action.salesforce_execute_soql.yml
    β”‚   └── schema/
    β”‚       └── salesforce_eca.schema.yml
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ EventSubscriber/
    β”‚   β”‚   └── SalesforceEcaEventSubscriber.php
    β”‚   └── Plugin/
    β”‚       β”œβ”€β”€ Action/
    β”‚       β”‚   └── SalesforceExecuteSoql.php
    β”‚       └── ECA/
    β”‚           β”œβ”€β”€ Condition/
    β”‚           β”œβ”€β”€ Event/
    β”‚           β”‚   β”œβ”€β”€ SalesforceEvents.php
    β”‚           β”‚   └── SalesforceEventsDeriver.php
    β”œβ”€β”€ salesforce_eca.info.yml
    β”œβ”€β”€ salesforce_eca.module
    β”œβ”€β”€ salesforce_eca.services.yml
    └── README.md
    

    Module Files

    salesforce_eca.info.yml: Defines the module metadata and dependencies, including:

    • salesforce:salesforce
    • salesforce:salesforce_push
    • salesforce:salesforce_mapping
    • eca:eca

    salesforce_eca.module: Implements hook_eca_actions_info_alter() to ensure our custom actions are properly categorized in the ECA UI.

    salesforce_eca.services.yml: Defines our EventSubscriber service which listens to Salesforce events and triggers corresponding ECA events.

    Event Integration

    The SalesforceEcaEventSubscriber class subscribes to all major Salesforce events (PUSH_ALLOWED, PUSH_PARAMS, PUSH_SUCCESS, PUSH_FAIL, PULL_PRESAVE, PULL_QUERY, PULL_PREPULL) and translates them into ECA events that can be used in ECA workflows.

    Event Plugin Implementation

    The SalesforceEvents class and its deriver define the Salesforce events for ECA, including salesforce:push_allowed, salesforce:push_params, salesforce:push_success, etc.

    Execute SOQL Query Action Implementation

    The main focus of this initial implementation is the SalesforceExecuteSoql action, which:

    1. Extends ECA's ConfigurableActionBase to properly integrate with the ECA system
    2. Provides two execution methods:
      • A structured approach using the SelectQuery object with RestClient::query()
      • A raw query option using RestClient::apiCall() for complex queries
    3. Offers a configurable form with:
      • A textarea for entering the SOQL query with token support
      • A checkbox to toggle between structured and raw query execution
      • A field to specify the token name where results will be stored
    4. Includes robust query parsing with extraction of object type, fields, conditions, ordering, limits, and offset
    5. Handles error conditions by catching exceptions and logging detailed error messages
    6. Implements access control by checking for the 'access salesforce' permission

    Future Considerations

    This implementation establishes a foundation for the Salesforce ECA integration. As we continue development, we should:

    1. Implement additional actions, conditions, and events as outlined in the proposed list
    2. Consider expanding token support across all aspects of the integration
    3. Ensure consistent error handling and permission checking across all actions
    4. Add comprehensive tests for all components

    The current implementation of the salesforce_execute_soql action serves as a template for other API-related actions in the proposed list, particularly those that interact with the Salesforce REST client.

  • πŸ‡ΊπŸ‡ΈUnited States camoa

    Initial Implementation of the Salesforce ECA Module and Execute SOQL Query Action

    This comment provides an overview of the initial implementation of the Salesforce ECA module, focusing on the first action we've created: salesforce_execute_soql.

    Module Structure

    We set up the following directory structure for the salesforce_eca module:

    salesforce_eca/
    β”œβ”€β”€ config/
    β”‚   β”œβ”€β”€ install/
    β”‚   β”‚   └── system.action.salesforce_execute_soql.yml
    β”‚   └── schema/
    β”‚       └── salesforce_eca.schema.yml
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ EventSubscriber/
    β”‚   β”‚   └── SalesforceEcaEventSubscriber.php
    β”‚   └── Plugin/
    β”‚       β”œβ”€β”€ Action/
    β”‚       β”‚   └── SalesforceExecuteSoql.php
    β”‚       └── ECA/
    β”‚           β”œβ”€β”€ Condition/
    β”‚           β”œβ”€β”€ Event/
    β”‚           β”‚   β”œβ”€β”€ SalesforceEvents.php
    β”‚           β”‚   └── SalesforceEventsDeriver.php
    β”œβ”€β”€ salesforce_eca.info.yml
    β”œβ”€β”€ salesforce_eca.module
    β”œβ”€β”€ salesforce_eca.services.yml
    └── README.md
    

    Module Files

    salesforce_eca.info.yml: Defines the module metadata and dependencies, including:

    • salesforce:salesforce
    • salesforce:salesforce_push
    • salesforce:salesforce_mapping
    • eca:eca

    salesforce_eca.module: Implements hook_eca_actions_info_alter() to ensure our custom actions are properly categorized in the ECA UI.

    salesforce_eca.services.yml: Defines our EventSubscriber service which listens to Salesforce events and triggers corresponding ECA events.

    Event Integration

    The SalesforceEcaEventSubscriber class subscribes to all major Salesforce events (PUSH_ALLOWED, PUSH_PARAMS, PUSH_SUCCESS, PUSH_FAIL, PULL_PRESAVE, PULL_QUERY, PULL_PREPULL) and translates them into ECA events that can be used in ECA workflows.

    Event Plugin Implementation

    The SalesforceEvents class and its deriver define the Salesforce events for ECA, including salesforce:push_allowed, salesforce:push_params, salesforce:push_success, etc.

    Execute SOQL Query Action Implementation

    The main focus of this initial implementation is the SalesforceExecuteSoql action, which:

    1. Extends ECA's ConfigurableActionBase to properly integrate with the ECA system
    2. Provides two execution methods:
      • A structured approach using the SelectQuery object with RestClient::query()
      • A raw query option using RestClient::apiCall() for complex queries
    3. Offers a configurable form with:
      • A textarea for entering the SOQL query with token support
      • A checkbox to toggle between structured and raw query execution
      • A field to specify the token name where results will be stored
    4. Includes robust query parsing with extraction of object type, fields, conditions, ordering, limits, and offset
    5. Handles error conditions by catching exceptions and logging detailed error messages
    6. Implements access control by checking for the 'access salesforce' permission

    Future Considerations

    This implementation establishes a foundation for the Salesforce ECA integration. As we continue development, we should:

    1. Implement additional actions, conditions, and events as outlined in the proposed list
    2. Consider expanding token support across all aspects of the integration
    3. Ensure consistent error handling and permission checking across all actions
    4. Add comprehensive tests for all components

    The current implementation of the salesforce_execute_soql action serves as a template for other API-related actions in the proposed list, particularly those that interact with the Salesforce REST client.

  • πŸ‡ΊπŸ‡ΈUnited States camoa

    I've been working on implementing the Salesforce ECA integration and wanted to share where things stand. Got pretty deep into this and there's some good progress to report, along with the usual bumps along the way.

    What's Actually Working

    Built out the full integration between Salesforce and ECA. The core pieces are in place and I've been able to test the main functionality in a real environment.

    ECA Events: All 11 Salesforce events now show up in the ECA interface. These cover the major operations - push success/fail, pull operations, delete permissions, etc. Used the deriver pattern that ECA expects, so each event gets its own plugin.

    SOQL Action: This turned out to be more interesting than expected. Built a custom ECA action that lets you run raw SOQL queries and store results in tokens. The tricky part was figuring out how to handle Salesforce's SObject responses - they don't play nice with ECA's token system out of the box. Had to convert them properly using the .fields() method.

    Field Discovery: Added something I didn't originally plan for. When you run a SOQL query, the action now tells you what fields are available. Helpful when you're working with custom objects or complex queries where you're not sure what Salesforce will return.

    Real Testing Results

    Set up a proper test environment with a Salesforce developer account and ngrok for OAuth (since you need a public callback URL). Here's what actually works:

    • Connected to Salesforce successfully through OAuth
    • Ran SELECT Id, Name, Email, Phone, CreatedDate, LastModifiedDate FROM Contact LIMIT 5
    • Got back 5 contact records with all the expected data
    • Token access working: [soql_results:records:0:Name] returns "Rose Gonzalez"
    • Field discovery shows: "Name, Email, Phone, CreatedDate, LastModifiedDate, Id"

    The token structure ended up being more complex than I initially thought. Records use numeric indexing (0, 1, 2...) and you need to use Salesforce's exact field names - capitalized like "Name" not "name".

    What Still Needs Work

    The push/pull events are implemented but not tested yet. They require setting up actual Salesforce mappings (Drupal entity to Salesforce object), which I haven't done. The events should fire when you create/update entities that are mapped to Salesforce, but that's the next testing phase.

    Also want to test the mapping ID filtering - the idea is you can have multiple Salesforce mappings and filter events to only specific ones.

    Technical Challenges I Hit

    Token serialization: ECA tokens need scalar values, but Salesforce returns complex objects. Spent time figuring out the right conversion approach. The .fields() method on SObject was key.

    Access control: Initially tried to use Salesforce permissions, but that breaks anonymous workflows. ECA actions generally allow access by default since the workflow controls execution - had to align with that pattern.

    Array indexing: Salesforce uses IDs as array keys, but ECA tokens work better with numeric indices. Added a conversion loop to fix this.

    Current Module Structure

    salesforce_eca/
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ EventSubscriber/SalesforceEcaEventSubscriber.php
    β”‚   └── Plugin/
    β”‚       β”œβ”€β”€ Action/SalesforceExecuteSoql.php
    β”‚       └── ECA/Event/
    β”‚           β”œβ”€β”€ SalesforceEvents.php
    β”‚           └── SalesforceEventsDeriver.php
    β”œβ”€β”€ config/ (schema and install configs)
    └── standard module files
    

    The event subscriber listens for Salesforce events and triggers corresponding ECA events. The SOQL action handles query execution and result processing.

    What's Next

    Need to create some Salesforce mappings and test the push/pull event integration. That's when we'll see if the event filtering and real-world workflows actually work as intended.

    The SOQL functionality is solid and ready to use. People can build ECA workflows that query Salesforce data right now. The push/pull integration needs that next testing phase, but the code is there.

    Anyone interested in testing this out? Would be helpful to get feedback on the approach and see how it works in different environments.

  • πŸ‡ΊπŸ‡ΈUnited States camoa

    Hey folks, wanted to update on this integration.

    Since there wasn't activity on the issue, I went ahead and released this as an independent module: https://www.drupal.org/project/salesforce_eca β†’

    The module is now available as an alpha release. It provides:
    - All Salesforce events exposed to ECA
    - SOQL query action
    - Trigger push/pull actions
    - Smart dependency handling for optional push/pull modules

    Happy to maintain it separately - this way people can start using it right away and iterate based on real-world usage.

    Closing this issue since the functionality is now available as a standalone module. Thanks for the Salesforce suite foundation that made this possible.

Production build 0.71.5 2024