Skip to content

Commit

Permalink
feat: add user track
Browse files Browse the repository at this point in the history
  • Loading branch information
remarkablemark committed Jul 21, 2022
1 parent bc4a249 commit d785dc4
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ This library currently supports a subset of the [Braze API endpoints](https://ww
- [ ] /users/external_ids/rename
- [ ] /users/external_ids/remove
- [ ] /users/identify
- [ ] /users/track
- [x] /users/track

### Send messages

Expand Down
21 changes: 21 additions & 0 deletions src/Braze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
CampaignsTriggerSendObject,
MessagesSendObject,
TransactionalV1CampaignsSendObject,
UsersTrackObject,
} from '.'
import { Braze } from '.'
import { request } from './common/request'
Expand Down Expand Up @@ -79,3 +80,23 @@ it('calls transactional.v1.campaigns.send()', async () => {
)
expect(mockedRequest).toBeCalledTimes(1)
})

it('calls users.track()', async () => {
mockedRequest.mockResolvedValueOnce(response)
expect(await braze.users.track(body as UsersTrackObject)).toBe(response)
expect(mockedRequest).toBeCalledWith(`${apiUrl}/users/track`, body, options)
expect(mockedRequest).toBeCalledTimes(1)
})

it('calls users.track() with bulk', async () => {
mockedRequest.mockResolvedValueOnce(response)
expect(await braze.users.track(body as UsersTrackObject, true)).toBe(response)
expect(mockedRequest).toBeCalledWith(`${apiUrl}/users/track`, body, {
...options,
headers: {
...options.headers,
'X-Braze-Bulk': 'true',
},
})
expect(mockedRequest).toBeCalledTimes(1)
})
6 changes: 6 additions & 0 deletions src/Braze.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as campaigns from './campaigns'
import * as messages from './messages'
import * as transactional from './transactional'
import * as users from './users'

export class Braze {
/**
Expand Down Expand Up @@ -51,4 +52,9 @@ export class Braze {
},
},
}

users = {
track: (body: users.UsersTrackObject, bulk?: boolean) =>
users.track(this.apiUrl, this.apiKey, body, bulk),
}
}
2 changes: 2 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type Comparison =

export type TriggerProperties = object

export type Properties = object

export type Attributes = object

export interface UserAlias {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './campaigns/trigger/types'
export * from './common/types'
export * from './messages/types'
export * from './transactional/v1/campaigns/types'
export * from './users/types'
2 changes: 2 additions & 0 deletions src/users/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './track'
export * from './types'
77 changes: 77 additions & 0 deletions src/users/track.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { post } from '../common/request'
import { track } from '.'
import type { UsersTrackObject } from './types'

jest.mock('../common/request')
const mockedPost = jest.mocked(post)

beforeEach(() => {
jest.clearAllMocks()
})

describe('/users/track', () => {
const apiUrl = 'https://rest.iad-01.braze.com'
const apiKey = 'apiKey'
const body: UsersTrackObject = {
attributes: [
{
external_id: 'user_identifier',
string_attribute: 'fruit',
boolean_attribute_1: true,
integer_attribute: 25,
array_attribute: ['banana', 'apple'],
},
],
events: [
{
external_id: 'user_identifier',
app_id: 'app_identifier',
name: 'watched_trailer',
time: '2013-07-16T19:20:30+1:00',
},
],
purchases: [
{
external_id: 'user_identifier',
app_id: 'app_identifier',
product_id: 'product_name',
currency: 'USD',
price: 12.12,
quantity: 6,
time: '2017-05-12T18:47:12Z',
properties: {
integer_property: 3,
string_property: 'Russell',
date_property: '2014-02-02T00:00:00Z',
},
},
],
}

const data = {}

it('calls request with url and body', async () => {
mockedPost.mockResolvedValueOnce(data)
expect(await track(apiUrl, apiKey, body)).toBe(data)
expect(mockedPost).toBeCalledWith(`${apiUrl}/users/track`, body, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
})
expect(mockedPost).toBeCalledTimes(1)
})

it('makes bulk update', async () => {
mockedPost.mockResolvedValueOnce(data)
expect(await track(apiUrl, apiKey, body, true)).toBe(data)
expect(mockedPost).toBeCalledWith(`${apiUrl}/users/track`, body, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
'X-Braze-Bulk': 'true',
},
})
expect(mockedPost).toBeCalledTimes(1)
})
})
36 changes: 36 additions & 0 deletions src/users/track.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { post } from '../common/request'
import type { UsersTrackObject } from './types'

/**
* User track.
*
* Use this endpoint to record custom events, purchases, and update user profile attributes.
*
* {@link https://www.braze.com/docs/api/endpoints/user_data/post_user_track/}
*
* @param apiUrl - Braze REST endpoint.
* @param apiKey - Braze API key.
* @param body - Request parameters.
* @param bulk - Bulk update.
* @returns - Braze response.
*/
export function track(apiUrl: string, apiKey: string, body: UsersTrackObject, bulk?: boolean) {
const options = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
}

if (bulk) {
;(options.headers as Record<string, string>)['X-Braze-Bulk'] = 'true'
}

return post(`${apiUrl}/users/track`, body, options) as Promise<{
message: string
attributes_processed?: number
events_processed?: number
purchases_processed?: number
errors?: object
}>
}
94 changes: 94 additions & 0 deletions src/users/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { Properties, UserAlias } from '../common/types'

/**
* Request body for user track.
*
* {@link https://www.braze.com/docs/api/endpoints/user_data/post_user_track/#request-body}
*/
export interface UsersTrackObject {
attributes?: UserAttributesObject[]
events?: EventObject[]
purchases?: PurchaseObject[]
}

/**
* User attributes object specification.
*
* {@link https://www.braze.com/docs/api/objects_filters/user_attributes_object}
*/
interface UserAttributesObject extends UserProfileField {
external_id?: string
user_alias?: UserAlias
braze_id?: string
_update_existing_only?: boolean
push_token_import?: boolean
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[custom_attribute: string]: any
}

/**
* Braze user profile fields.
*
* {@link https://www.braze.com/docs/api/objects_filters/user_attributes_object}
*/
interface UserProfileField {
country?: string
current_location?: {
longitude: number
latitude: number
}
date_of_first_session?: string
date_of_last_session?: string
dob?: string
email?: string
email_subscribe?: 'opted_in' | 'unsubscribed' | 'subscribed'
email_open_tracking_disabled?: boolean
email_click_tracking_disabled?: boolean
external_id?: string
facebook?: string
first_name?: string
gender?: 'M' | 'F' | 'O' | 'N' | 'P' | null
home_city?: string
language?: string
last_name?: string
marked_email_as_spam_at?: string
phone?: string
push_subscribe?: 'opted_in' | 'unsubscribed' | 'subscribed'
time_zone?: string
twitter?: string
}

/**
* Event object specification.
*
* {@link https://www.braze.com/docs/api/objects_filters/event_object/}
*/
interface EventObject {
external_id?: string
user_alias?: UserAlias
braze_id?: string
app_id?: string
name: string
time: string
properties?: Properties
_update_existing_only?: boolean
}

/**
* Purchase object specification.
*
* {@link https://www.braze.com/docs/api/objects_filters/purchase_object/}
*/
interface PurchaseObject {
external_id?: string
user_alias?: UserAlias
braze_id?: string
app_id: string
product_id: string
currency: string
price: number
quantity?: number
time: string
properties?: Properties
_update_existing_only?: boolean
}

0 comments on commit d785dc4

Please sign in to comment.