From 3f821a67025fa155088047f6fca66e7e8a378f08 Mon Sep 17 00:00:00 2001 From: Corentin Ardeois Date: Tue, 21 May 2024 11:27:26 -0400 Subject: [PATCH] [Amplitude] add device_manufacturer from user agent (#2025) For some devices (iPhones for instance), Amplitude requires a `device_manufacturer` property in order to compute their native property `Device Type` and `Device Family` According to [their SDK](https://github.com/amplitude/Amplitude-TypeScript/blob/v1.x/packages/plugin-user-agent-enrichment-browser/src/user-agent-enrichment-plugin.ts#L26-L38) I've done the following changes: - added `device_manufacturer` based on `device.vendor` - updated `device_model` to first use `device.model` before falling back to `os.name`. As we can see in the snapshots tests, the `device_model` property makes more sense to be `iPhone` instead of `iOS` (which is already the `os_name` --- .../amplitude/__tests__/amplitude.test.ts | 478 +++++++++--------- .../amplitude/__tests__/user-agent.test.ts | 16 + .../src/destinations/amplitude/user-agent.ts | 4 +- 3 files changed, 264 insertions(+), 234 deletions(-) diff --git a/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts b/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts index 1ae81cd038..af62c5f743 100644 --- a/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts +++ b/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts @@ -324,6 +324,7 @@ describe('Amplitude', () => { "events": Array [ Object { "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, "device_model": "Mac OS", "device_type": undefined, "event_properties": Object {}, @@ -371,7 +372,8 @@ describe('Amplitude', () => { "city": "San Francisco", "country": "United States", "device_id": "julio", - "device_model": "iOS", + "device_manufacturer": "Apple", + "device_model": "iPhone", "device_type": "mobile", "event_properties": Object {}, "event_type": "Test Event", @@ -417,35 +419,36 @@ describe('Amplitude', () => { expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "city": "San Francisco", - "country": "United States", - "device_id": "julio", - "device_model": "iOS", - "device_type": "mobile", - "event_properties": Object {}, - "event_type": "Test Event", - "ip": "8.8.8.8", - "language": "en-US", - "library": "segment", - "location_lat": 40.2964197, - "location_lng": -76.9411617, - "os_name": "iOS", - "os_version": "9", - "platform": "Web", - "session_id": 1234567890, - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "city": "San Francisco", + "country": "United States", + "device_id": "julio", + "device_manufacturer": "Apple", + "device_model": "iPhone", + "device_type": "mobile", + "event_properties": Object {}, + "event_type": "Test Event", + "ip": "8.8.8.8", + "language": "en-US", + "library": "segment", + "location_lat": 40.2964197, + "location_lng": -76.9411617, + "os_name": "iOS", + "os_version": "9", + "platform": "Web", + "session_id": 1234567890, + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('should send data to the EU endpoint', async () => { @@ -533,27 +536,28 @@ describe('Amplitude', () => { expect(responses[0].status).toBe(200) expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", - "device_model": "Mac OS", - "device_type": undefined, - "event_properties": Object {}, - "event_type": "Test Event", - "library": "segment", - "os_name": "iPhone OS", - "os_version": "8.1.3", - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, + "device_model": "Mac OS", + "device_type": undefined, + "event_properties": Object {}, + "event_type": "Test Event", + "library": "segment", + "os_name": "iPhone OS", + "os_version": "8.1.3", + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('should calculate revenue based on price and quantity', async () => { @@ -1038,27 +1042,28 @@ describe('Amplitude', () => { expect(responses[0].status).toBe(200) expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", - "device_model": "Mac OS", - "device_type": undefined, - "event_properties": Object {}, - "event_type": "Test Event", - "library": "segment", - "os_name": "Mac OS", - "os_version": "53", - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, + "device_model": "Mac OS", + "device_type": undefined, + "event_properties": Object {}, + "event_type": "Test Event", + "library": "segment", + "os_name": "Mac OS", + "os_version": "53", + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('should support session_id from `integrations.Actions Amplitude.session_id`', async () => { @@ -1083,35 +1088,36 @@ describe('Amplitude', () => { expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "city": "San Francisco", - "country": "United States", - "device_id": "julio", - "device_model": "iOS", - "device_type": "mobile", - "event_properties": Object {}, - "event_type": "Test Event", - "ip": "8.8.8.8", - "language": "en-US", - "library": "segment", - "location_lat": 40.2964197, - "location_lng": -76.9411617, - "os_name": "iOS", - "os_version": "9", - "platform": "Web", - "session_id": 1234567890, - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "city": "San Francisco", + "country": "United States", + "device_id": "julio", + "device_manufacturer": "Apple", + "device_model": "iPhone", + "device_type": "mobile", + "event_properties": Object {}, + "event_type": "Test Event", + "ip": "8.8.8.8", + "language": "en-US", + "library": "segment", + "location_lat": 40.2964197, + "location_lng": -76.9411617, + "os_name": "iOS", + "os_version": "9", + "platform": "Web", + "session_id": 1234567890, + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('supports session_id from `integrations.Actions Amplitude.session_id` in number format', async () => { @@ -1136,35 +1142,36 @@ describe('Amplitude', () => { expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "city": "San Francisco", - "country": "United States", - "device_id": "julio", - "device_model": "iOS", - "device_type": "mobile", - "event_properties": Object {}, - "event_type": "Test Event", - "ip": "8.8.8.8", - "language": "en-US", - "library": "segment", - "location_lat": 40.2964197, - "location_lng": -76.9411617, - "os_name": "iOS", - "os_version": "9", - "platform": "Web", - "session_id": 1234567890, - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "city": "San Francisco", + "country": "United States", + "device_id": "julio", + "device_manufacturer": "Apple", + "device_model": "iPhone", + "device_type": "mobile", + "event_properties": Object {}, + "event_type": "Test Event", + "ip": "8.8.8.8", + "language": "en-US", + "library": "segment", + "location_lat": 40.2964197, + "location_lng": -76.9411617, + "os_name": "iOS", + "os_version": "9", + "platform": "Web", + "session_id": 1234567890, + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('should send data to the EU endpoint', async () => { @@ -1252,27 +1259,28 @@ describe('Amplitude', () => { expect(responses[0].status).toBe(200) expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", - "device_model": "Mac OS", - "device_type": undefined, - "event_properties": Object {}, - "event_type": "Test Event", - "library": "segment", - "os_name": "iPhone OS", - "os_version": "8.1.3", - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, + "device_model": "Mac OS", + "device_type": undefined, + "event_properties": Object {}, + "event_type": "Test Event", + "library": "segment", + "os_name": "iPhone OS", + "os_version": "8.1.3", + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) }) @@ -1538,27 +1546,28 @@ describe('Amplitude', () => { expect(responses[0].status).toBe(200) expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", - "device_model": "Mac OS", - "device_type": undefined, - "event_properties": Object {}, - "event_type": "Test Event", - "library": "segment", - "os_name": "Mac OS", - "os_version": "53", - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, + "device_model": "Mac OS", + "device_type": undefined, + "event_properties": Object {}, + "event_type": "Test Event", + "library": "segment", + "os_name": "Mac OS", + "os_version": "53", + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('supports session_id from `integrations.Actions Amplitude.session_id`', async () => { @@ -1583,35 +1592,36 @@ describe('Amplitude', () => { expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "city": "San Francisco", - "country": "United States", - "device_id": "julio", - "device_model": "iOS", - "device_type": "mobile", - "event_properties": Object {}, - "event_type": "Test Event", - "ip": "8.8.8.8", - "language": "en-US", - "library": "segment", - "location_lat": 40.2964197, - "location_lng": -76.9411617, - "os_name": "iOS", - "os_version": "9", - "platform": "Web", - "session_id": 1234567890, - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "city": "San Francisco", + "country": "United States", + "device_id": "julio", + "device_manufacturer": "Apple", + "device_model": "iPhone", + "device_type": "mobile", + "event_properties": Object {}, + "event_type": "Test Event", + "ip": "8.8.8.8", + "language": "en-US", + "library": "segment", + "location_lat": 40.2964197, + "location_lng": -76.9411617, + "os_name": "iOS", + "os_version": "9", + "platform": "Web", + "session_id": 1234567890, + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('supports session_id from `integrations.Actions Amplitude.session_id` in number format', async () => { @@ -1636,35 +1646,36 @@ describe('Amplitude', () => { expect(responses[0].data).toMatchObject({}) expect(responses[0].options.json).toMatchInlineSnapshot(` - Object { - "api_key": undefined, - "events": Array [ - Object { - "city": "San Francisco", - "country": "United States", - "device_id": "julio", - "device_model": "iOS", - "device_type": "mobile", - "event_properties": Object {}, - "event_type": "Test Event", - "ip": "8.8.8.8", - "language": "en-US", - "library": "segment", - "location_lat": 40.2964197, - "location_lng": -76.9411617, - "os_name": "iOS", - "os_version": "9", - "platform": "Web", - "session_id": 1234567890, - "time": 1629213675449, - "use_batch_endpoint": false, - "user_id": "user1234", - "user_properties": Object {}, - }, - ], - "options": undefined, - } - `) + Object { + "api_key": undefined, + "events": Array [ + Object { + "city": "San Francisco", + "country": "United States", + "device_id": "julio", + "device_manufacturer": "Apple", + "device_model": "iPhone", + "device_type": "mobile", + "event_properties": Object {}, + "event_type": "Test Event", + "ip": "8.8.8.8", + "language": "en-US", + "library": "segment", + "location_lat": 40.2964197, + "location_lng": -76.9411617, + "os_name": "iOS", + "os_version": "9", + "platform": "Web", + "session_id": 1234567890, + "time": 1629213675449, + "use_batch_endpoint": false, + "user_id": "user1234", + "user_properties": Object {}, + }, + ], + "options": undefined, + } + `) }) it('sends data to the EU endpoint', async () => { @@ -1854,6 +1865,7 @@ describe('Amplitude', () => { "events": Array [ Object { "device_id": "6fd32a7e-3c56-44c2-bd32-62bbec44c53d", + "device_manufacturer": undefined, "device_model": "Mac OS", "device_type": undefined, "event_properties": Object {}, @@ -1937,7 +1949,7 @@ describe('Amplitude', () => { "api_key", "undefined", "identification", - "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_model\\":\\"iOS\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", + "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_manufacturer\\":\\"Apple\\",\\"device_model\\":\\"iPhone\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", "options", "undefined", ], @@ -2011,7 +2023,7 @@ describe('Amplitude', () => { "api_key", "undefined", "identification", - "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_model\\":\\"iOS\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", + "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_manufacturer\\":\\"Apple\\",\\"device_model\\":\\"iPhone\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", "options", "undefined", ], @@ -2198,7 +2210,7 @@ describe('Amplitude', () => { "api_key", "", "identification", - "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_model\\":\\"iOS\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", + "{\\"os_name\\":\\"iOS\\",\\"os_version\\":\\"9\\",\\"device_manufacturer\\":\\"Apple\\",\\"device_model\\":\\"iPhone\\",\\"device_type\\":\\"mobile\\",\\"user_id\\":\\"some-user-id\\",\\"device_id\\":\\"some-anonymous-id\\",\\"user_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"country\\":\\"United States\\",\\"city\\":\\"San Francisco\\",\\"language\\":\\"en-US\\",\\"platform\\":\\"Web\\",\\"library\\":\\"segment\\"}", "options", "undefined", ], diff --git a/packages/destination-actions/src/destinations/amplitude/__tests__/user-agent.test.ts b/packages/destination-actions/src/destinations/amplitude/__tests__/user-agent.test.ts index e0dae894d3..0af71526c5 100644 --- a/packages/destination-actions/src/destinations/amplitude/__tests__/user-agent.test.ts +++ b/packages/destination-actions/src/destinations/amplitude/__tests__/user-agent.test.ts @@ -15,6 +15,7 @@ describe('amplitude - custom user agent parsing', () => { const result = parseUserAgentProperties(userAgent, userAgentData) expect(result).toEqual({ + device_manufacturer: undefined, os_name: 'Android', os_version: '5.0.1', device_model: 'TAB 2 A7', @@ -27,6 +28,7 @@ describe('amplitude - custom user agent parsing', () => { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36' const result = parseUserAgentProperties(userAgent) expect(result).toEqual({ + device_manufacturer: undefined, device_model: 'Mac OS', device_type: undefined, os_name: 'Mac OS', @@ -51,10 +53,24 @@ describe('amplitude - custom user agent parsing', () => { const result = parseUserAgentProperties(userAgent, userAgentData) expect(result).toEqual({ + device_manufacturer: undefined, device_model: 'SM-J710FN', device_type: undefined, os_name: 'Mac OS', os_version: '12.6.1' }) }) + + it('should parse custom user for iphone strings', () => { + const userAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1' + const result = parseUserAgentProperties(userAgent) + expect(result).toEqual({ + device_manufacturer: 'Apple', + device_model: 'iPhone', + device_type: 'mobile', + os_name: 'iOS', + os_version: '16' + }) + }) }) diff --git a/packages/destination-actions/src/destinations/amplitude/user-agent.ts b/packages/destination-actions/src/destinations/amplitude/user-agent.ts index 0aa492c756..b4c13a4a3a 100644 --- a/packages/destination-actions/src/destinations/amplitude/user-agent.ts +++ b/packages/destination-actions/src/destinations/amplitude/user-agent.ts @@ -5,6 +5,7 @@ interface ParsedUA { os_version?: string device_model?: string device_type?: string + device_manufacturer?: string } interface UserAgentData { @@ -29,7 +30,8 @@ export function parseUserAgentProperties(userAgent?: string, userAgentData?: Use return { os_name: os.name ?? browser.name, os_version: userAgentData?.platformVersion ?? browser.major, - device_model: userAgentData?.model ?? os.name, + device_manufacturer: device.vendor, + device_model: userAgentData?.model ?? device.model ?? os.name, device_type: device.type } }