0

Hello I have an AWS Step Function that is triggered by a webhook event. The initial Input is an array of objects and I am using Map to split the input and from each to run the rest of the workflow.

My issue is that I am unsure how to pass the original input and make this part of the object in the Map state.

Here is the code I have so far

  const processDeals = new Map(scope, 'Process Each Deal', {
    itemsPath: JsonPath.stringAt('$'),
    resultPath: JsonPath.DISCARD,
  });

  const getContactsForDeal = new CustomState(scope, 'Fetch Contacts for a Deal', {
    stateJson: {
      Type: 'Task',
      Resource: 'arn:aws:states:::http:invoke',
      Parameters: {
        'ApiEndpoint.$': JsonPath.format(
          'https://api.hubapi.com/crm/v4/objects/deal/{}/associations/contact',
          JsonPath.stringAt('$.objectId')
        ),
        Method: 'GET',
        Authentication: {
          ConnectionArn: connectionArn,
        },
      },
      Retry: [
        {
          ErrorEquals: ['States.Http.StatusCode.400'],
          MaxAttempts: 0,
        },
        {
          ErrorEquals: ['States.ALL'],
          IntervalSeconds: 5,
          MaxAttempts: 3,
          BackoffRate: 2,
        },
      ],
      ResultSelector: {
        'results.$': '$.ResponseBody.results',
      },
      ResultPath: '$.contactsForDeal',
    },
  });

  const processContacts = new Map(scope, 'Process Each Contact', {
    itemsPath: JsonPath.stringAt('$.contactsForDeal.results'),
    resultPath: JsonPath.DISCARD,
  });

  const getContactDetails = new CustomState(scope, 'Get Contact Details', {
    stateJson: {
      Type: 'Task',
      Resource: 'arn:aws:states:::http:invoke',
      Parameters: {
        'ApiEndpoint.$': JsonPath.format(
          'https://api.hubapi.com/crm/v3/objects/contacts/{}',
          JsonPath.stringAt('$.toObjectId')
        ),
        Method: 'GET',
        Authentication: {
          ConnectionArn: connectionArn,
        },
      },
      Retry: [
        {
          ErrorEquals: ['States.Http.StatusCode.400'],
          MaxAttempts: 0,
        },
        {
          ErrorEquals: ['States.ALL'],
          IntervalSeconds: 5,
          MaxAttempts: 3,
          BackoffRate: 2,
        },
      ],
      ResultSelector: {
        'id.$': '$.ResponseBody.properties.hs_object_id',
        'firstname.$': '$.ResponseBody.properties.firstname',
        'lastname.$': '$.ResponseBody.properties.lastname',
        'email.$': '$.ResponseBody.properties.email',
      },
      ResultPath: '$.contactDetails',
    },
  });

  const sendEventToBus = new EventBridgePutEvents(scope, 'Send Contact Event to Bus', {
    entries: [
      {
        eventBus: localBus,
        source: Aws.ACCOUNT_ID,
        detailType: 'Contact Details Processed',
        detail: TaskInput.fromObject({
          'contactDetails.$': '$.contactDetails',
        }),
      },
    ],
    resultPath: JsonPath.DISCARD,
  });

  getContactsForDeal.next(processContacts);
  processContacts.itemProcessor(getContactDetails.next(sendEventToBus));
  processDeals.itemProcessor(getContactsForDeal);

  const definition = processDeals.next(jobSuccess);

My initial Output is like:

{
  "occurredAt": 1737546168873,
  "subscriptionType": "deal.propertyChange",
  "attemptNumber": 0,
  "objectId": 20062945626,
  "propertyName": "dealstage",
  "propertyValue": "8429273",
  "changeSource": "CRM_UI",
  "ResponseBody": {
    "results": [
      {
        "toObjectId": 29806791006,
        "associationTypes": [
          {
            "category": "HUBSPOT_DEFINED",
            "typeId": 3,
            "label": null
          }
        ]
      },
      {
        "toObjectId": 90112604675,
        "associationTypes": [
          {
            "category": "HUBSPOT_DEFINED",
            "typeId": 3,
            "label": null
          }
        ]
      }
    ]
  }
}

How do I include in the Process Each Contact Map the

  "occurredAt": 1737546168873,
  "subscriptionType": "deal.propertyChange",
  "attemptNumber": 0,
  "objectId": 20062945626,
  "propertyName": "dealstage",
  "propertyValue": "8429273",
  "changeSource": "CRM_UI",

data, so that this can be sent and be part of the contactDetails?

  const processContacts = new Map(scope, 'Process Each Contact', {
    itemsPath: JsonPath.stringAt('$.contactsForDeal.results'),
    resultPath: JsonPath.DISCARD,
  });

Any advice is much appreciated

1 Answer 1

1

This is what the ItemSelector is for. It allows you to shape the input to each iteration in a flexible way, including by selecting data from outside the specific item.

"ItemSelector": {
  "contact.$": "$$.Map.Item.Value",
  "occurredAt.$": "$.occurredAt",
  "subscriptionType.$": "$.subscriptionType",
  "attemptNumber.$": "$.attemptNumber",
  "objectId.$": "$.objectId",
  "propertyName.$": "$.propertyName",
  "propertyValue.$": "$.propertyValue",
  "changeSource.$": "$.changeSource"
}

By adjusting your payload structure earlier in the state machine (e.g., by ensuring the common fields are nested under a single and not at the same level as the item array), you could probably simplify this to avoid repeating all these fields. Or using the recently released JSONata support, you could shape it with a lot of flexibility. Regardless though, the ItemSelector is the feature you need.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.