Skip to main content

Salesforce: Handling Recursion and Nested Hierarchies

With an example that retrieves a product category tree from Salesforce B2B Core APIs, this document describes how you can use Sub Components to implement recursion with Conscia to access hierarchical data structures in third-party products. Many third-party APIs provide access to hierarchical data one level at a time. Calling code must retrieve the child nodes for a root node and then retrieve the child nodes of those child nodes and then retrieve the child nodes of those grandchild nodes, continuing to some level of depth or until there are no more descendants. Where an API does not offer a direct way to retrieve an entire hierarchy, solutions can use recursion to access the entire node tree. This scenario is common in generating website navigation elements or similar hierarchical structures when the API provider does not offer a comprehensive hierarchy endpoint.

Implementing Recursion in Conscia

Conscia enables the implementation of recursive logic through the use of Sub Components in the DX Engine. A root component calls the third-party API to determine the children of the root node. A "level one" Sub Component of the root component determines the children of each child of the root (the grandchildren of the root node). A Sub Component of the "level one" Sub Compoennt (the "level two" Sub Component) determines the children of those grandchildren (the great-grandchildren of the root node). This configuration repeates to the depth required for the solution.

Typically, each of these components uses the same API from the vendor that accepts as a parameter the identifier of the node for which to retrieve child nodes. To retrieve the children of the root node, the root component does not specify a node or explicitly specify the root node in the API call. Each of the Sub Components specifies the node for which to retrieve children.

Data Reduction with Object Mapper

An Object Mapper component simplifies the resultant data hierarchy by including only the essential values needed by consumers, thereby enhancing data manageability and performance.

Performance Considerations

The performance of recursive solutions in Conscia is influenced by the hierarchy's breadth and depth. For example, starting from a single root node, if each node has ten children, the API calls multiply at each level, significantly impacting response times. Additionally, Conscia processes Sub Components sequentially, not in parallel, making it crucial to optimize performance by caching the response of the Object Mapper component. We expect that the customer would run this type of a process as part of their build process such as static site generation in a JAMStack architecture, and not in real-time.

Debugging in Conscia

Sub Components do not appear on the Visualizer tab of the Conscia debugger. Information regarding Sub Components and their interactions can be found on the Debug Response tab.

Salesforce Example

The Salesforce Composable Storefront provides a practical example of managing product categories with up to four levels of nesting (root > children > grandchildren > great-grandchildren). This recipe involves five Conscia components:

  1. Root Component: Identifies top-level product categories (children).
  2. Level One Component: Determines second-level product subcategories (grandchildren).
  3. Level Two Component: Identifies third-level product subcategories (great-grandchildren).
  4. Level Three Component: Determines fourth-level product subcategories (great-great-grandchildren).
  5. Object Mapper: Simplifies the JSON response from the Salesforce API.

alt_text

The solution uses a Conscia context field named categoryId to pass product category identifiers to the Salesforce API through the parentProductCategoryId query string parameter described in the following section.

Salesforce API Details

These components utilize the Salesforce "Commerce Webstore Product Categories Children" API, with each level passing product category IDs to retrieve respective subcategories.

  • API URL: https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/connect_resources_commerce_webstore_product_categories_children.htm
  • Endpoint: /commerce/webstores/{webstoreId}/product-categories/children
  • Query Parameters:
    • parentProductCategoryId: ID of the product category for which to retrieve child product categories.
    • fields: Names of the fields of the product category record to retrieve.

Recursive Component Invocation

The root component specifies the level one component as a Sub Component. Conscia invokes the level one Sub Component for each category ID in the JSON retrieved from Salesforce by the root component. This pattern continues down the hierarchy, with each level specifying the next as a Sub Component and passing along the necessary identifiers.

In all cases, the component passes Name as the fields parameter to the API in order to retrieve only the product category name. This minimizes the response payload and hence optimizes peformace while increasing component usability for developers.

Calling clients can invoke the Object Mapper component without passing any context data. The Object Mapper component invokes the root component on which it depends. The root component invokes its level one Sub Component once for each child product category in the response from Salesforce.

Response Transformation

The Salesforce API returns data in the following format:

"productCategories": [
{
"bannerImage": null,
"fields": {
"Description": null,
"Name": "Products"
},
"id": "0ZGHr0000004qQZOAY",
"mediaGroups": [],
"tileImage": null,
"urlName": null
},
{
"bannerImage": null,
"fields": {
"Description": null,
"Name": "Electronics"
},
"id": "0ZGHr0000004wZZOAY",
"mediaGroups": [],
"tileImage": null,
"urlName": null
}
]

Each component other than the Object Mapper specifies the following Response Transformation that eliminates an the unnecessary productCategories structures from the JSON response retrieved from Salesforce:

response.productCategories

Sub Components

The root, level one, and level two sub components specify a Property Name of subCatgory, which cause them to include the results of their API calls in the JSON as an entry with this key. Each of these Components set the categoryId context field to response.id, which maps to the product category ID of each child product category returned by the API call to salesforce.

alt_text

Object Mapper

The Object Mapper component restructures the consolidated JSON response under a key named navigation in the JSON response.

alt_text

The Evaluation Expression used in this object mapper includes only the names and IDs of the subcategories, as well as the subCategory keys:

{
"id": id,
"Name": fields.Name,
"subCategory": subCategory.`sf-b2b-nav-l1`.response.{
"id": id,
"Name": fields.Name,
"subCategory": subCategory.`sf-b2b-nav-l2`.response.{
"id": id,
"Name": fields.Name,
"subCategory": subCategory.`sf-b2b-nav-l3`.response.{
"id": id,
"Name": fields.Name
}
}
}
}

The following JSON fragment demonstrates the resulting structure.

{
"navigation": {
"id": "0ZGHr0000004qQZOAY",
"Name": "Products",
"subCategory": [
{
"id": "0ZGHr0000004qQeOAI",
"Name": "First Level Category One"
},
{
"id": "0ZGHr0000004qQfOAI",
"Name": "Second Level Category One",
"subCategory": [
{
"id": "0ZGHr0000004qQgOAI",
"Name": "Third Level Category One",
"subCategory": {
"id": "0ZGHr0000004wZjOAI",
"Name": "Fourth Level Category One"
}
},
{
"id": "0ZGHr0000004qQhOAI",
"Name": "Third level Category Two"
}
]
},
{
"id": "0ZGHr0000004sIkOAI",
"Name": "First Level Category Two"
}
]
}
}