Skip to content

Commit 2afb2d2

Browse files
[Wingify] - new Cloud Mode Destination (#3861)
* [Wingify] - nee Destination * removing unnecessary exports * removing file * [Wingify] Address Copilot review comments - utility.ts: trim + prefix event name consistently (no whitespace leak, no double `segment.` prefix); URL-encode the `en` query param; fall back to the US host for unknown regions. - syncAudience: drop `required` on the `name` field (unused in perform and absent from identify events); fix "An unique" -> "A unique"; rename the shadowing local `action` to `audienceAction`; US host fallback; reformat types.ts to 2-space indentation. - Add a syncAudience test for identify events with no `event` field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [Wingify] Remove unused name field from syncAudience The `name` field was never referenced in `perform` (the payload hardcodes `wingify_integration`), so remove it from the action definition, generated types, and metadata. Also syncs the stale metadata.json entry for `userId`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [Wingify] Drop name from syncAudience preset mapping The Sync Audience preset still mapped `name` to `$.event`, but the action no longer defines a `name` field. Remove it so the preset mapping only references fields that exist on the action. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * updating metadata --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 78edefd commit 2afb2d2

19 files changed

Lines changed: 2708 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { createTestIntegration } from '@segment/actions-core'
2+
import Destination from '../index'
3+
import type { Settings } from '../generated-types'
4+
import { generateUUIDFor } from '../utility'
5+
6+
const testDestination = createTestIntegration(Destination)
7+
8+
describe('Wingify AccountID Validation', () => {
9+
describe('testAuthentication', () => {
10+
it('should validate authentication inputs', async () => {
11+
const settings: Settings = {
12+
wingifyAccountId: 654331,
13+
region: 'US'
14+
}
15+
await expect(testDestination.testAuthentication(settings)).resolves.not.toThrowError()
16+
})
17+
18+
it('should throw error for invalid AccountId', async () => {
19+
const settings: Settings = {
20+
wingifyAccountId: 65431231,
21+
region: 'US'
22+
}
23+
await expect(testDestination.testAuthentication(settings)).rejects.toThrow(
24+
'Invalid AccountID. Please check your AccountID'
25+
)
26+
})
27+
})
28+
})
29+
30+
describe('UUID Generator', () => {
31+
describe('method: generateFor', () => {
32+
it('should return desired UUID for userId and accountId', () => {
33+
expect(generateUUIDFor('Varun', 12345)).toBe('C4D95C097902569F9A2D2E87CD3201C8')
34+
expect(generateUUIDFor('Alice', 12345)).toBe('E3B732864F315FB6974BC3EF4E2FD920')
35+
expect(generateUUIDFor('__123__', 12345)).toBe('50A5B167FB6356A796F91D8951E480EE')
36+
expect(generateUUIDFor('We@#dcs3232.f3', 12345)).toBe('AAB4580A6BB3525FAA31DC341752D501')
37+
})
38+
})
39+
})

packages/destination-actions/src/destinations/wingify/generated-types.ts

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
import nock from 'nock'
2+
import { createTestEvent, createTestIntegration } from '@segment/actions-core'
3+
import Destination from '../../index'
4+
5+
const testDestination = createTestIntegration(Destination)
6+
7+
const BASE_ENDPOINT = 'https://collect.wingify.net'
8+
const accountId = 654331
9+
const wingifyUuid = 'ABC123'
10+
const SDK_KEY = 'sample-api-key'
11+
const SANITISED_USERID = '57CC1A3D57215E67824E461010E43F53'
12+
13+
describe('Wingify.identifyUser Web', () => {
14+
describe('Only required parameters', () => {
15+
it('should send segment traits as Wingify attributes', async () => {
16+
const event = createTestEvent({
17+
traits: {
18+
wingify_uuid: wingifyUuid,
19+
textProperty: 'Hello'
20+
}
21+
})
22+
nock(BASE_ENDPOINT).post(`/events/t?en=wingify_syncVisitorProp&a=${accountId}`).reply(200, {})
23+
const responses = await testDestination.testAction('identifyUser', {
24+
event,
25+
useDefaultMappings: true,
26+
settings: {
27+
wingifyAccountId: accountId
28+
}
29+
})
30+
const page = event.context?.page
31+
const expectedRequest = {
32+
d: {
33+
visId: wingifyUuid,
34+
event: {
35+
props: {
36+
$visitor: {
37+
props: {
38+
'segment.textProperty': 'Hello'
39+
}
40+
},
41+
wingifyMeta: {
42+
source: 'segment.cloud'
43+
},
44+
page,
45+
isCustomEvent: true
46+
},
47+
name: 'wingify_syncVisitorProp'
48+
},
49+
visitor: {
50+
props: {
51+
'segment.textProperty': 'Hello'
52+
}
53+
}
54+
}
55+
}
56+
expect(responses[0].status).toBe(200)
57+
expect(responses[0].options.json).toMatchObject(expectedRequest)
58+
expect(responses[0].options.headers).toMatchInlineSnapshot(`
59+
Headers {
60+
Symbol(map): Object {
61+
"user-agent": Array [
62+
"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",
63+
],
64+
"x-forwarded-for": Array [
65+
"8.8.8.8",
66+
],
67+
},
68+
}
69+
`)
70+
})
71+
})
72+
73+
describe('All parameters', () => {
74+
it('should send segment traits as Wingify attributes', async () => {
75+
const event = createTestEvent({
76+
traits: {
77+
wingify_uuid: wingifyUuid,
78+
textProperty: 'Hello'
79+
},
80+
context: {
81+
page: {
82+
url: 'www.abc.com'
83+
},
84+
ip: '0.0.0.0',
85+
userAgent: 'Segment'
86+
},
87+
timestamp: '2023-05-09T13:12:44.924Z'
88+
})
89+
nock(BASE_ENDPOINT).post(`/events/t?en=wingify_syncVisitorProp&a=${accountId}`).reply(200, {})
90+
const responses = await testDestination.testAction('identifyUser', {
91+
event,
92+
useDefaultMappings: true,
93+
settings: {
94+
wingifyAccountId: accountId
95+
}
96+
})
97+
const page = event.context?.page
98+
const expectedRequest = {
99+
d: {
100+
visId: wingifyUuid,
101+
event: {
102+
props: {
103+
$visitor: {
104+
props: {
105+
'segment.textProperty': 'Hello'
106+
}
107+
},
108+
wingifyMeta: {
109+
source: 'segment.cloud'
110+
},
111+
page,
112+
isCustomEvent: true
113+
},
114+
name: 'wingify_syncVisitorProp'
115+
},
116+
visitor: {
117+
props: {
118+
'segment.textProperty': 'Hello'
119+
}
120+
}
121+
}
122+
}
123+
expect(responses[0].status).toBe(200)
124+
expect(responses[0].options.json).toMatchObject(expectedRequest)
125+
expect(responses[0].options.headers).toMatchInlineSnapshot(`
126+
Headers {
127+
Symbol(map): Object {
128+
"user-agent": Array [
129+
"Segment",
130+
],
131+
"x-forwarded-for": Array [
132+
"0.0.0.0",
133+
],
134+
},
135+
}
136+
`)
137+
})
138+
})
139+
})
140+
141+
describe('Wingify.identifyUser Fullstack', () => {
142+
describe('Only required parameters', () => {
143+
it('should send segment traits as Wingify attributes', async () => {
144+
const event = createTestEvent({
145+
traits: {
146+
wingify_uuid: wingifyUuid,
147+
textProperty: 'Hello'
148+
}
149+
})
150+
nock(BASE_ENDPOINT).post(`/events/t?en=wingify_syncVisitorProp&a=${accountId}`).reply(200, {})
151+
const responses = await testDestination.testAction('identifyUser', {
152+
event,
153+
useDefaultMappings: true,
154+
settings: {
155+
wingifyAccountId: accountId,
156+
apikey: SDK_KEY
157+
}
158+
})
159+
const page = event.context?.page
160+
const expectedRequest = {
161+
d: {
162+
visId: SANITISED_USERID,
163+
event: {
164+
props: {
165+
$visitor: {
166+
props: {
167+
'segment.textProperty': 'Hello',
168+
wingify_fs_environment: 'sample-api-key'
169+
}
170+
},
171+
wingifyMeta: {
172+
source: 'segment.cloud'
173+
},
174+
page,
175+
isCustomEvent: true
176+
},
177+
name: 'wingify_syncVisitorProp'
178+
},
179+
visitor: {
180+
props: {
181+
'segment.textProperty': 'Hello',
182+
wingify_fs_environment: 'sample-api-key'
183+
}
184+
}
185+
}
186+
}
187+
expect(responses[0].status).toBe(200)
188+
expect(responses[0].options.json).toMatchObject(expectedRequest)
189+
expect(responses[0].options.headers).toMatchInlineSnapshot(`
190+
Headers {
191+
Symbol(map): Object {
192+
"user-agent": Array [
193+
"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",
194+
],
195+
"x-forwarded-for": Array [
196+
"8.8.8.8",
197+
],
198+
},
199+
}
200+
`)
201+
})
202+
})
203+
204+
describe('All parameters', () => {
205+
it('should send segment traits as Wingify attributes', async () => {
206+
const event = createTestEvent({
207+
traits: {
208+
wingify_uuid: wingifyUuid,
209+
textProperty: 'Hello'
210+
},
211+
context: {
212+
page: {
213+
url: 'www.abc.com'
214+
},
215+
ip: '0.0.0.0',
216+
userAgent: 'Segment'
217+
},
218+
timestamp: '2023-05-09T13:12:44.924Z'
219+
})
220+
nock(BASE_ENDPOINT).post(`/events/t?en=wingify_syncVisitorProp&a=${accountId}`).reply(200, {})
221+
const responses = await testDestination.testAction('identifyUser', {
222+
event,
223+
useDefaultMappings: true,
224+
settings: {
225+
wingifyAccountId: accountId,
226+
apikey: SDK_KEY
227+
}
228+
})
229+
const page = event.context?.page
230+
const expectedRequest = {
231+
d: {
232+
visId: SANITISED_USERID,
233+
event: {
234+
props: {
235+
$visitor: {
236+
props: {
237+
'segment.textProperty': 'Hello',
238+
wingify_fs_environment: 'sample-api-key'
239+
}
240+
},
241+
wingifyMeta: {
242+
source: 'segment.cloud'
243+
},
244+
page,
245+
isCustomEvent: true
246+
},
247+
name: 'wingify_syncVisitorProp'
248+
},
249+
visitor: {
250+
props: {
251+
'segment.textProperty': 'Hello',
252+
wingify_fs_environment: 'sample-api-key'
253+
}
254+
}
255+
}
256+
}
257+
expect(responses[0].status).toBe(200)
258+
expect(responses[0].options.json).toMatchObject(expectedRequest)
259+
expect(responses[0].options.headers).toMatchInlineSnapshot(`
260+
Headers {
261+
Symbol(map): Object {
262+
"user-agent": Array [
263+
"Segment",
264+
],
265+
"x-forwarded-for": Array [
266+
"0.0.0.0",
267+
],
268+
},
269+
}
270+
`)
271+
})
272+
})
273+
})

packages/destination-actions/src/destinations/wingify/identifyUser/generated-types.ts

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)