11import { ModifiedResponse , RequestClient } from '@segment/actions-core'
22import 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
55class 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 > (
0 commit comments