Skip to content

Conversation

@abhandage
Copy link
Contributor

@abhandage abhandage commented Aug 28, 2025

This pull request introduces support for batching multiple contact info payloads into a single request for the DV360 "addToAudContactInfo" action. It refactors the editContactInfo function to enable batching logic, improves code structure with helper functions, and adds comprehensive test coverage for the new batching behavior.

Batching support and refactoring:

  • Refactored the editContactInfo function in functions.ts to support batching: now, when enable_batching is true, multiple payloads are combined into a single API request, improving efficiency and reducing the number of requests sent.
  • Introduced helper functions buildContactInfoList and buildRequestPayload to simplify and clarify payload construction for both batched and individual requests.
  • Updated logic to handle both batched and non-batched requests, including validation and error handling for required fields.

Testing improvements:

  • Added a new test in index.test.ts to verify that multiple payloads are correctly batched into a single request when enable_batching is set, including utilities for generating test events and contact lists.

Testing

Tested with actions-tester

curl --location 'http://localhost:3000/addToAudContactInfo' \
--header 'accept: */*' \
--header 'accept-language: en-US,en;q=0.9' \
--header 'connection: keep-alive' \
--header 'content-type: application/json' \
--header 'origin: https://app.segment.com' \
--header 'sec-ch-ua: "Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--header 'sec-fetch-dest: empty' \
--header 'sec-fetch-mode: cors' \
--header 'sec-fetch-site: cross-site' \
--header 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' \
--data-raw '{
  "mapping": {
    "emails": {
      "@path": "$.context.traits.emails"
    },
    "phoneNumbers": {
      "@path": "$.context.traits.phoneNumbers"
    },
    "zipCodes": {
      "@path": "$.context.traits.zipCodes"
    },
    "firstName": {
      "@path": "$.context.traits.firstName"
    },
    "lastName": {
      "@path": "$.context.traits.lastName"
    },
    "countryCode": {
      "@template": "india"
    },
    "external_id": {
      "@path": "$.context.personas.external_audience_id"
    },
    "advertiser_id": {
      "@path": "$.context.personas.audience_settings.advertiserId"
    },
    "enable_batching": true,
    "batch_size": 500000
  },
  "settings": {},
  "payload": [
    {
      "timestamp": "2025-09-01T12:52:07.106Z",
      "type": "track",
      "email": "[email protected]",
      "properties": {
        "property1": 1,
        "property2": "test",
        "property3": true
      },
      "userId": "test-user-yvdmwit1yh",
      "event": "Segment Test Event Name",
      "anonymousId": "1l9q9qu4jtj",
      "context": {
        "active": true,
        "app": {
          "name": "InitechGlobal",
          "version": "545",
          "build": "3.0.1.545",
          "namespace": "com.production.segment"
        },
        "personas": {
          "external_audience_id": "9197244337",
          "audience_settings": {
            "advertiserId": "1109207483"
          }
        },
        "traits": {
          "emails": "[email protected]",
          "zipCodes": "122022",
          "countryCode": "+91",
          "firstName": "prithviraj",
          "lastName": "rathore"
        },
        "campaign": {
          "name": "TPS Innovation Newsletter",
          "source": "Newsletter",
          "medium": "email",
          "term": "tps reports",
          "content": "image link"
        },
        "device": {
          "id": "B5372DB0-C21E-11E4-8DFC-AA07A5B093DB",
          "advertisingId": "7A3CBEA0-BDF5-11E4-8DFC-AA07A5B093DB",
          "adTrackingEnabled": true,
          "manufacturer": "Apple",
          "model": "iPhone7,2",
          "name": "maguro",
          "type": "ios",
          "token": "ff15bc0c20c4aa6cd50854ff165fd265c838e5405bfeb9571066395b8c9da449"
        },
        "ip": "8.8.8.8",
        "library": {
          "name": "analytics.js",
          "version": "2.11.1"
        },
        "locale": "en-US",
        "location": {
          "city": "San Francisco",
          "country": "United States",
          "latitude": 40.2964197,
          "longitude": -76.9411617,
          "speed": 0
        },
        "network": {
          "bluetooth": false,
          "carrier": "T-Mobile US",
          "cellular": true,
          "wifi": false
        },
        "os": {
          "name": "iPhone OS",
          "version": "8.1.3"
        },
        "page": {
          "path": "/academy/",
          "referrer": "",
          "search": "",
          "title": "Analytics Academy",
          "url": "https://segment.com/academy/"
        },
        "referrer": {
          "id": "ABCD582CDEFFFF01919",
          "type": "dataxu"
        },
        "screen": {
          "width": 320,
          "height": 568,
          "density": 2
        },
        "groupId": "12345",
        "timezone": "Europe/Amsterdam",
        "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
      },
      "receivedAt": "2025-09-01T12:52:07.106Z",
      "sentAt": "2025-09-01T12:52:07.106Z",
      "version": 2
    },
    {
      "timestamp": "2025-09-01T12:52:07.106Z",
      "type": "track",
      "email": "[email protected]",
      "properties": {
        "property1": 1,
        "property2": "test",
        "property3": true
      },
      "userId": "test-user-yvdmwit1yh",
      "event": "Segment Test Event Name",
      "anonymousId": "1l9q9qu4jtj",
      "context": {
        "active": true,
        "app": {
          "name": "InitechGlobal",
          "version": "545",
          "build": "3.0.1.545",
          "namespace": "com.production.segment"
        },
        "personas": {
          "external_audience_id": "9197244337",
          "audience_settings": {
            "advertiserId": "1109207483"
          }
        },
        "traits": {
          "emails": "[email protected]",
          "zipCodes": "122022",
          "countryCode": "+91",
          "firstName": "prithviraj1234",
          "lastName": "rathore1234"
        },
        "campaign": {
          "name": "TPS Innovation Newsletter",
          "source": "Newsletter",
          "medium": "email",
          "term": "tps reports",
          "content": "image link"
        },
        "device": {
          "id": "B5372DB0-C21E-11E4-8DFC-AA07A5B093DB",
          "advertisingId": "7A3CBEA0-BDF5-11E4-8DFC-AA07A5B093DB",
          "adTrackingEnabled": true,
          "manufacturer": "Apple",
          "model": "iPhone7,2",
          "name": "maguro",
          "type": "ios",
          "token": "ff15bc0c20c4aa6cd50854ff165fd265c838e5405bfeb9571066395b8c9da449"
        },
        "ip": "8.8.8.8",
        "library": {
          "name": "analytics.js",
          "version": "2.11.1"
        },
        "locale": "en-US",
        "location": {
          "city": "San Francisco",
          "country": "United States",
          "latitude": 40.2964197,
          "longitude": -76.9411617,
          "speed": 0
        },
        "network": {
          "bluetooth": false,
          "carrier": "T-Mobile US",
          "cellular": true,
          "wifi": false
        },
        "os": {
          "name": "iPhone OS",
          "version": "8.1.3"
        },
        "page": {
          "path": "/academy/",
          "referrer": "",
          "search": "",
          "title": "Analytics Academy",
          "url": "https://segment.com/academy/"
        },
        "referrer": {
          "id": "ABCD582CDEFFFF01919",
          "type": "dataxu"
        },
        "screen": {
          "width": 320,
          "height": 568,
          "density": 2
        },
        "groupId": "12345",
        "timezone": "Europe/Amsterdam",
        "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
      },
      "receivedAt": "2025-09-01T12:52:07.106Z",
      "sentAt": "2025-09-01T12:52:07.106Z",
      "version": 2
    }
  ]
}'
[
  {
    "request": {
      "url": "https://displayvideo.googleapis.com/v4/firstPartyAndPartnerAudiences/9197244337:editCustomerMatchMembers",
      "method": "POST",
      "headers": {
        "authorization": "<redacted>",
        "content-type": "application/json; charset=utf-8",
        "user-agent": "Segment (Actions)"
      },
      "body": "{\"advertiserId\":\"1109207483\",\"addedContactInfoList\":{\"contactInfos\":[{\"hashedEmails\":\"73643ef63aecb5646f02ce622d408e11efb064258160765b66e4903b30422e87\",\"zipCodes\":\"122022\",\"hashedFirstName\":\"b2d2ea56b310be50d3c9b18e3cf10f15625587e142fef78e21b92a306cf2e960\",\"hashedLastName\":\"5f841bfb2b45656bc2de84162087b85cf09ab671f514dd1e96b4d25197c4c6ef\",\"countryCode\":\"+91\"}],\"consent\":{\"adUserData\":\"CONSENT_STATUS_GRANTED\",\"adPersonalization\":\"CONSENT_STATUS_GRANTED\"}}}"
    },
    "response": {
      "statusCode": 200,
      "statusMessage": "OK",
      "headers": {
        "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
        "connection": "close",
        "content-encoding": "gzip",
        "content-type": "application/json; charset=UTF-8",
        "date": "Tue, 02 Sep 2025 05:28:52 GMT",
        "server": "ESF",
        "transfer-encoding": "chunked",
        "vary": "Origin, X-Origin, Referer",
        "x-content-type-options": "nosniff",
        "x-frame-options": "SAMEORIGIN",
        "x-xss-protection": "0"
      },
      "body": {
        "firstPartyAndPartnerAudienceId": "9197244337"
      }
    }
  },
  {
    "request": {
      "url": "https://displayvideo.googleapis.com/v4/firstPartyAndPartnerAudiences/9197244337:editCustomerMatchMembers",
      "method": "POST",
      "headers": {
        "authorization": "<redacted>",
        "content-type": "application/json; charset=utf-8",
        "user-agent": "Segment (Actions)"
      },
      "body": "{\"advertiserId\":\"1109207483\",\"addedContactInfoList\":{\"contactInfos\":[{\"hashedEmails\":\"73643ef63aecb5646f02ce622d408e11efb064258160765b66e4903b30422e87\",\"zipCodes\":\"122022\",\"hashedFirstName\":\"b2d2ea56b310be50d3c9b18e3cf10f15625587e142fef78e21b92a306cf2e960\",\"hashedLastName\":\"5f841bfb2b45656bc2de84162087b85cf09ab671f514dd1e96b4d25197c4c6ef\",\"countryCode\":\"+91\"}],\"consent\":{\"adUserData\":\"CONSENT_STATUS_GRANTED\",\"adPersonalization\":\"CONSENT_STATUS_GRANTED\"}}}"
    },
    "response": {
      "statusCode": 200,
      "statusMessage": "OK",
      "headers": {
        "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
        "connection": "close",
        "content-encoding": "gzip",
        "content-type": "application/json; charset=UTF-8",
        "date": "Tue, 02 Sep 2025 05:34:54 GMT",
        "server": "ESF",
        "transfer-encoding": "chunked",
        "vary": "Origin, X-Origin, Referer",
        "x-content-type-options": "nosniff",
        "x-frame-options": "SAMEORIGIN",
        "x-xss-protection": "0"
      },
      "body": {
        "firstPartyAndPartnerAudienceId": "9197244337"
      }
    }
  },
  {
    "request": {
      "url": "https://displayvideo.googleapis.com/v4/firstPartyAndPartnerAudiences/9197244337:editCustomerMatchMembers",
      "method": "POST",
      "headers": {
        "authorization": "<redacted>",
        "content-type": "application/json; charset=utf-8",
        "user-agent": "Segment (Actions)"
      },
      "body": "{\"advertiserId\":\"1109207483\",\"addedContactInfoList\":{\"contactInfos\":[{\"hashedEmails\":\"73643ef63aecb5646f02ce622d408e11efb064258160765b66e4903b30422e87\",\"zipCodes\":\"122022\",\"hashedFirstName\":\"b2d2ea56b310be50d3c9b18e3cf10f15625587e142fef78e21b92a306cf2e960\",\"hashedLastName\":\"5f841bfb2b45656bc2de84162087b85cf09ab671f514dd1e96b4d25197c4c6ef\",\"countryCode\":\"+91\"},{\"hashedEmails\":\"95a5b07af1f177bb25bd75169c21d307d732c48fa17db1fd239f32846bf30290\",\"zipCodes\":\"122022\",\"hashedFirstName\":\"d29eedb22554e5ab9e597025e383973c347b8d89c24e5bcdc88bd53f055c2d8a\",\"hashedLastName\":\"b12191d419b038580cb80d5590804a92cb6decaa62690c3b18265ea8b1634b9f\",\"countryCode\":\"+91\"}],\"consent\":{\"adUserData\":\"CONSENT_STATUS_GRANTED\",\"adPersonalization\":\"CONSENT_STATUS_GRANTED\"}}}"
    },
    "response": {
      "statusCode": 200,
      "statusMessage": "OK",
      "headers": {
        "alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
        "connection": "close",
        "content-encoding": "gzip",
        "content-type": "application/json; charset=UTF-8",
        "date": "Tue, 02 Sep 2025 05:38:51 GMT",
        "server": "ESF",
        "transfer-encoding": "chunked",
        "vary": "Origin, X-Origin, Referer",
        "x-content-type-options": "nosniff",
        "x-frame-options": "SAMEORIGIN",
        "x-xss-protection": "0"
      },
      "body": {
        "firstPartyAndPartnerAudienceId": "9197244337"
      }
    }
  }
]
  • Added unit tests for new functionality
  • Tested end-to-end using the local server
  • [If destination is already live] Tested for backward compatibility of destination. Note: New required fields are a breaking change.
  • [Segmenters] Tested in the staging environment
  • [Segmenters] [If applicable for this change] Tested for regression with Hadron.

@codecov
Copy link

codecov bot commented Aug 28, 2025

Codecov Report

❌ Patch coverage is 66.66667% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.70%. Comparing base (67a677e) to head (a2cc031).
⚠️ Report is 17 commits behind head on main.

Files with missing lines Patch % Lines
...ns/src/destinations/first-party-dv360/functions.ts 66.66% 2 Missing and 3 partials ⚠️

❌ Your patch status has failed because the patch coverage (66.66%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3211      +/-   ##
==========================================
+ Coverage   79.62%   79.70%   +0.07%     
==========================================
  Files        1177     1184       +7     
  Lines       21703    21813     +110     
  Branches     4220     4248      +28     
==========================================
+ Hits        17282    17386     +104     
- Misses       3679     3684       +5     
- Partials      742      743       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@abhandage abhandage requested a review from Copilot August 28, 2025 09:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements batching support for the editContactInfo function in the First Party DV360 destination, allowing multiple contact info updates to be processed in a single API request for improved efficiency.

Key Changes:

  • Refactored editContactInfo function to support both batched and individual processing modes
  • Added helper functions to improve code organization and reusability
  • Enhanced test coverage with batching-specific test cases

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/destination-actions/src/destinations/first-party-dv360/functions.ts Refactored editContactInfo function to support batching with helper functions for payload building
packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts Added test coverage for batching functionality with helper functions for creating batch test events

Comment on lines 179 to 198
// Assume all payloads are for the same audience/advertiser (use first)
const { external_id: audienceId, advertiser_id: advertiserId } = validPayloads[0]
if (!audienceId || !advertiserId) {
throw new IntegrationError(
'Missing required audience or advertiser ID for batch request',
'MISSING_REQUIRED_FIELD',
400
)
}
const contactInfos = validPayloads.map(processPayload)
const contactInfoList = buildContactInfoList(contactInfos)
const requestPayload = buildRequestPayload(advertiserId, contactInfoList, operation)
const endpoint = DV360API + '/' + audienceId + ':editCustomerMatchMembers'
const response = await request<DV360editCustomerMatchResponse>(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: requestPayload
})
statsContext?.statsClient?.incr('addCustomerMatchMembers.success', contactInfos.length, statsContext?.tags)
return response.data
Copy link

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

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

The batching logic assumes all payloads belong to the same audience/advertiser by using only the first payload's IDs. This could lead to incorrect behavior if payloads have different external_id or advertiser_id values. Consider validating that all payloads have the same IDs or grouping them accordingly.

Copilot uses AI. Check for mistakes.
@abhandage abhandage marked this pull request as ready for review September 2, 2025 06:01
@abhandage abhandage requested a review from a team as a code owner September 2, 2025 06:01

statsContext?.statsClient?.incr('addCustomerMatchMembers.success', 1, statsContext?.tags)
return response.data
const batchingEnabled = payloads.some((p) => p.enable_batching)
Copy link
Contributor

Choose a reason for hiding this comment

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

It is guranted that all payloads will have the save enable_batching value. So, this check isn't required. Also, payloads is always an array.

@itsarijitray itsarijitray merged commit 56fc074 into main Sep 4, 2025
13 of 14 checks passed
@itsarijitray itsarijitray deleted the STRATCONN-6152-fix-batching-dv360 branch September 4, 2025 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants