Skip to content

Commit d087243

Browse files
Merge branch 'segmentio:main' into feature/add-presets-to-pipedrive-destination
2 parents 4327b0a + b93a98c commit d087243

209 files changed

Lines changed: 13227 additions & 8443 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/required-field-check.yml

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,38 @@ jobs:
3333
uses: actions/github-script@v7
3434
with:
3535
script: |
36-
const response = await github.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/files', {
36+
const files = await github.paginate('GET /repos/{owner}/{repo}/pulls/{pull_number}/files', {
3737
owner: context.repo.owner,
3838
repo: context.repo.repo,
39-
pull_number: context.issue.number
40-
})
41-
const files = response.data.map(file => file.filename)
42-
core.setOutput('files', files.join(' '))
39+
pull_number: context.issue.number,
40+
per_page: 100
41+
}, response => response.data.map(file => file.filename))
42+
core.setOutput('files', JSON.stringify(files))
4343
if (files.length === 0) {
4444
core.setOutput('no_files_changed', true)
4545
}
4646
4747
- name: Find required fields on current branch
4848
id: required-fields-curr-branch
4949
if: steps.get_files.outputs.no_files_changed != 'true'
50+
env:
51+
CHANGED_FILES: ${{ steps.get_files.outputs.files }}
5052
run: |
51-
set -e
52-
REQUIRED_FIELDS_CURR_BRANCH=$(./bin/run list-required-fields -p ${{ steps.get_files.outputs.files }} | jq -c .)
53+
set -euo pipefail
54+
ARGS=()
55+
while IFS= read -r file; do
56+
ARGS+=(-p "$file")
57+
done < <(printf '%s' "$CHANGED_FILES" | jq -r '.[]')
58+
REQUIRED_FIELDS_CURR_BRANCH=$(./bin/run list-required-fields "${ARGS[@]}" | jq -ce .)
5359
echo "REQUIRED_FIELDS_CURR_BRANCH=$REQUIRED_FIELDS_CURR_BRANCH" >> $GITHUB_ENV
5460
5561
- name: Check for required fields on main branch
5662
id: required-fields-main-branch
5763
if: steps.get_files.outputs.no_files_changed != 'true'
64+
env:
65+
CHANGED_FILES: ${{ steps.get_files.outputs.files }}
5866
run: |
59-
set -e
67+
set -euo pipefail
6068
git checkout main
6169
# Run install again on main branch to ensure all dependencies are installed
6270
# and the lockfile is up to date
@@ -65,7 +73,11 @@ jobs:
6573
# checked against the correct version of the dependencies
6674
yarn install --frozen-lockfile --silent
6775
# Run the list-required-fields command on the main branch to get the required fields
68-
REQUIRED_FIELDS_MAIN_BRANCH=$(./bin/run list-required-fields -p ${{ steps.get_files.outputs.files }} | jq -c .)
76+
ARGS=()
77+
while IFS= read -r file; do
78+
ARGS+=(-p "$file")
79+
done < <(printf '%s' "$CHANGED_FILES" | jq -r '.[]')
80+
REQUIRED_FIELDS_MAIN_BRANCH=$(./bin/run list-required-fields "${ARGS[@]}" | jq -ce .)
6981
echo "REQUIRED_FIELDS_MAIN_BRANCH=$REQUIRED_FIELDS_MAIN_BRANCH" >> $GITHUB_ENV
7082
7183
- name: Add comment on PR if there is diff in required fields
@@ -82,34 +94,29 @@ jobs:
8294
const requiredFieldsOnMain = JSON.parse(process.env.REQUIRED_FIELDS_MAIN_BRANCH)
8395
8496
Object.keys(requiredFieldsOnBranch).forEach(key => {
85-
// Check if key is present in requiredFieldsOnMain
86-
if(requiredFieldsOnMain[key]) {
87-
const getActionKeys = Object.keys(requiredFieldsOnBranch[key])
88-
for(const actionKey of getActionKeys) {
89-
const branchRequiredFields = requiredFieldsOnBranch[key][actionKey]
90-
const mainRequiredFields = requiredFieldsOnMain[key][actionKey]
91-
const diff = branchRequiredFields.filter(field => !mainRequiredFields?.includes(field))
92-
if(diff.length > 0) {
93-
const isSettingsKey = actionKey === 'settings'
94-
if (isSettingsKey) {
95-
fieldsAdded.push(`- **Destination**: ${key}, **Settings**:${diff.join(',')}`)
96-
} else {
97-
fieldsAdded.push(`- **Destination**: ${key}, Action **Field(s)**:${diff.join(',')}`)
98-
}
99-
}
97+
// Skip new destinations — no existing customers to break
98+
if(!requiredFieldsOnMain[key]) {
99+
return
100+
}
101+
102+
const getActionKeys = Object.keys(requiredFieldsOnBranch[key])
103+
for(const actionKey of getActionKeys) {
104+
// Skip new actions — no existing configurations to break
105+
if(!requiredFieldsOnMain[key][actionKey]) {
106+
continue
100107
}
101-
} else {
102-
103-
// If key is not present in requiredFieldsOnMain, then all fields are added recently
104-
const getActionKeys = Object.keys(requiredFieldsOnBranch[key])
105-
for(const actionKey of getActionKeys) {
106-
const branchRequiredFields = requiredFieldsOnBranch[key][actionKey]
107-
if(actionKey === 'settings') {
108-
fieldsAdded.push(`- **Destination**: ${key}, **Settings**:${branchRequiredFields.join(',')}`)
108+
109+
const branchRequiredFields = requiredFieldsOnBranch[key][actionKey]
110+
const mainRequiredFields = requiredFieldsOnMain[key][actionKey]
111+
const diff = branchRequiredFields.filter(field => !mainRequiredFields.includes(field))
112+
if(diff.length > 0) {
113+
const isSettingsKey = actionKey === 'settings'
114+
if (isSettingsKey) {
115+
fieldsAdded.push(`- **Destination**: ${key}, **Settings**:${diff.join(',')}`)
109116
} else {
110-
fieldsAdded.push(`- **Destination**: ${key}, **Action**:${actionKey}, **Fields**:${branchRequiredFields.join(',')}`)
117+
fieldsAdded.push(`- **Destination**: ${key}, Action **Field(s)**:${diff.join(',')}`)
111118
}
112-
}
119+
}
113120
}
114121
})
115122
}

packages/actions-shared/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@segment/actions-shared",
33
"description": "Shared destination action methods and definitions.",
4-
"version": "1.150.0",
4+
"version": "1.152.0",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/segmentio/action-destinations",
@@ -37,7 +37,7 @@
3737
},
3838
"dependencies": {
3939
"@amplitude/ua-parser-js": "^0.7.25",
40-
"@segment/actions-core": "^3.166.0",
40+
"@segment/actions-core": "^3.167.0",
4141
"cheerio": "^1.0.0-rc.10",
4242
"dayjs": "^1.10.7",
4343
"escape-goat": "^3",

packages/actions-shared/src/dotdigital/api/resources/dd-contact-api.ts

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ModifiedResponse, RequestClient } from '@segment/actions-core'
22
import DDApi from '../dd-api'
3-
import { Contact, ChannelIdentifier, Identifiers, ChannelProperties, UpsertContactJSON, DataFields } from '../types'
3+
import { Contact, ChannelIdentifier, Identifiers, ResubscribeOptions, ChannelProperties, UpsertContactJSON, DataFields } from '../types'
44

55
class DDContactApi extends DDApi {
66
constructor(api_host: string, client: RequestClient) {
@@ -36,17 +36,116 @@ class DDContactApi extends DDApi {
3636

3737
/**
3838
* Creates or updates a contact .
39+
* @returns {Promise<Contact>} A promise resolving to the contact data.
40+
*/
41+
public async upsertContact(payload: {
42+
channelIdentifier: string
43+
emailIdentifier?: string
44+
mobileNumberIdentifier?: string
45+
emailType?: string
46+
optInType?: string
47+
updateEmailSubscription?: boolean
48+
emailSubscriptionStatus?: string
49+
emailResubscribe?: boolean
50+
resubscribeWithoutChallengeEmail?: boolean
51+
preferredLocale?: string
52+
redirectUrlAfterChallenge?: string
53+
updateSmsSubscription?: boolean
54+
smsSubscriptionStatus?: string
55+
listId?: number
56+
dataFields?: { [k: string]: unknown }
57+
}): Promise<Contact> {
58+
const {
59+
channelIdentifier,
60+
emailIdentifier,
61+
mobileNumberIdentifier,
62+
emailType = 'html',
63+
optInType = 'single',
64+
updateEmailSubscription = true,
65+
emailSubscriptionStatus = 'subscribed',
66+
emailResubscribe = false,
67+
resubscribeWithoutChallengeEmail = false,
68+
preferredLocale,
69+
redirectUrlAfterChallenge,
70+
updateSmsSubscription = true,
71+
smsSubscriptionStatus = 'subscribed',
72+
listId,
73+
dataFields
74+
} = payload
75+
76+
const idValue = channelIdentifier === 'email' ? emailIdentifier : mobileNumberIdentifier
77+
78+
const identifiers: Identifiers = {
79+
...(emailIdentifier && { email: emailIdentifier }),
80+
...(mobileNumberIdentifier && { mobileNumber: mobileNumberIdentifier })
81+
}
82+
83+
const channelProperties: ChannelProperties = {}
84+
85+
// Email channel properties
86+
if (emailIdentifier) {
87+
channelProperties.email = {
88+
emailType,
89+
optInType
90+
}
91+
92+
if (updateEmailSubscription) {
93+
if (emailSubscriptionStatus === 'subscribed') {
94+
// Only send status if resubscribe is enabled
95+
if (emailResubscribe) {
96+
channelProperties.email.status = 'subscribed'
97+
98+
const resubscribeOptions: ResubscribeOptions = {
99+
resubscribeWithNoChallenge: resubscribeWithoutChallengeEmail
100+
}
101+
102+
if (!resubscribeWithoutChallengeEmail) {
103+
if (preferredLocale) resubscribeOptions.preferredLocale = preferredLocale
104+
if (redirectUrlAfterChallenge) resubscribeOptions.redirectUrlAfterChallenge = redirectUrlAfterChallenge
105+
}
106+
107+
channelProperties.email.resubscribeOptions = resubscribeOptions
108+
}
109+
} else if (emailSubscriptionStatus) {
110+
// For unsubscribed/suppressed, always send the status
111+
channelProperties.email.status = emailSubscriptionStatus
112+
}
113+
}
114+
}
115+
116+
// SMS channel properties
117+
if (mobileNumberIdentifier && updateSmsSubscription && smsSubscriptionStatus) {
118+
channelProperties.sms = {
119+
status: smsSubscriptionStatus
120+
}
121+
}
122+
123+
const data: UpsertContactJSON = {
124+
identifiers,
125+
channelProperties,
126+
...(listId && { lists: [listId] }),
127+
dataFields: dataFields as DataFields
128+
}
129+
130+
const response: ModifiedResponse<Contact> = await this.patch<Contact, UpsertContactJSON>(
131+
`/contacts/v3/${channelIdentifier}/${idValue}?merge-option=overwrite`,
132+
data
133+
)
134+
135+
return response.data
136+
}
137+
138+
/**
139+
* Unsubscribes a contact .
39140
* @param {Payload} payload - The event payload.
40141
* @returns {Promise<Contact>} A promise resolving to the contact data.
41142
*/
42-
public async upsertContact(payload: {
43-
channelIdentifier: string,
44-
emailIdentifier?: string,
45-
mobileNumberIdentifier?: string,
46-
listId: number,
47-
dataFields?: {[k: string]: unknown}
143+
public async unsubscribeContact(payload: {
144+
channelIdentifier: string
145+
emailIdentifier?: string
146+
mobileNumberIdentifier?: string
48147
}): Promise<Contact> {
49-
const { channelIdentifier, emailIdentifier, mobileNumberIdentifier, listId, dataFields } = payload
148+
const { channelIdentifier, emailIdentifier, mobileNumberIdentifier } = payload
50149

51150
const idValue = channelIdentifier === 'email' ? emailIdentifier : mobileNumberIdentifier
52151

@@ -57,22 +156,16 @@ class DDContactApi extends DDApi {
57156

58157
const channelProperties: ChannelProperties = {
59158
...(emailIdentifier && {
60-
email: {
61-
status: 'subscribed',
62-
emailType: 'html',
63-
optInType: 'single'
64-
}
159+
email: { status: 'unsubscribed' }
65160
}),
66161
...(mobileNumberIdentifier && {
67-
sms: { status: 'subscribed' }
162+
sms: { status: 'unsubscribed' }
68163
})
69164
}
70165

71166
const data: UpsertContactJSON = {
72167
identifiers,
73-
channelProperties,
74-
lists: [listId],
75-
dataFields: dataFields as DataFields
168+
channelProperties
76169
}
77170

78171
const response: ModifiedResponse<Contact> = await this.patch<Contact, UpsertContactJSON>(

packages/actions-shared/src/dotdigital/api/types.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,17 @@ export interface DataFields {
4040
[key: string]: string | number | boolean | null
4141
}
4242

43+
export interface ResubscribeOptions {
44+
resubscribeWithNoChallenge?: boolean
45+
preferredLocale?: string
46+
redirectUrlAfterChallenge?: string
47+
}
4348
export interface ChannelProperties {
4449
email?: {
45-
status: string
46-
emailType: string
47-
optInType: string
50+
status?: string
51+
emailType?: string
52+
optInType?: string
53+
resubscribeOptions?: ResubscribeOptions
4854
}
4955
sms?: {
5056
status: string
@@ -103,7 +109,7 @@ export type ChannelIdentifier = { email: string; 'mobileNumber'?: never } | { 'm
103109
export interface UpsertContactJSON {
104110
identifiers: Identifiers
105111
channelProperties: ChannelProperties
106-
lists: number[]
112+
lists?: number[]
107113
dataFields?: DataFields
108114
}
109115

packages/browser-destination-runtime/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@segment/browser-destination-runtime",
3-
"version": "1.95.0",
3+
"version": "1.96.0",
44
"license": "MIT",
55
"publishConfig": {
66
"access": "public",
@@ -62,7 +62,7 @@
6262
}
6363
},
6464
"dependencies": {
65-
"@segment/actions-core": "^3.166.0"
65+
"@segment/actions-core": "^3.167.0"
6666
},
6767
"devDependencies": {
6868
"@segment/analytics-next": "*"

packages/browser-destinations/destinations/1flow/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@segment/analytics-browser-actions-1flow",
3-
"version": "1.79.0",
3+
"version": "1.80.0",
44
"license": "MIT",
55
"publishConfig": {
66
"access": "public",
@@ -15,7 +15,7 @@
1515
},
1616
"typings": "./dist/esm",
1717
"dependencies": {
18-
"@segment/browser-destination-runtime": "^1.95.0"
18+
"@segment/browser-destination-runtime": "^1.96.0"
1919
},
2020
"peerDependencies": {
2121
"@segment/analytics-next": ">=1.55.0"

packages/browser-destinations/destinations/adobe-target/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@segment/analytics-browser-actions-adobe-target",
3-
"version": "1.97.0",
3+
"version": "1.98.0",
44
"license": "MIT",
55
"publishConfig": {
66
"access": "public",
@@ -16,7 +16,7 @@
1616
},
1717
"typings": "./dist/esm",
1818
"dependencies": {
19-
"@segment/browser-destination-runtime": "^1.95.0"
19+
"@segment/browser-destination-runtime": "^1.96.0"
2020
},
2121
"peerDependencies": {
2222
"@segment/analytics-next": ">=1.55.0"

packages/browser-destinations/destinations/algolia-plugins/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@segment/analytics-browser-actions-algolia-plugins",
3-
"version": "1.73.0",
3+
"version": "1.74.0",
44
"license": "MIT",
55
"publishConfig": {
66
"access": "public",
@@ -15,7 +15,7 @@
1515
},
1616
"typings": "./dist/esm",
1717
"dependencies": {
18-
"@segment/browser-destination-runtime": "^1.95.0"
18+
"@segment/browser-destination-runtime": "^1.96.0"
1919
},
2020
"peerDependencies": {
2121
"@segment/analytics-next": ">=1.55.0"

0 commit comments

Comments
 (0)