@engage_so/core 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts DELETED
@@ -1,365 +0,0 @@
1
- import { Buffer } from 'buffer'
2
- import fetch from 'cross-fetch'
3
- import { EngageError } from './error'
4
-
5
- if (typeof btoa === 'undefined') {
6
- global.btoa = function (str) {
7
- return Buffer.from(str).toString('base64')
8
- }
9
- }
10
-
11
- export interface Key {
12
- key?: string
13
- secret?: string
14
- }
15
- export interface EventParameter {
16
- event: string
17
- value?: string | number | Date | boolean
18
- properties?: {
19
- [key: string]: string | number | Date | boolean
20
- }
21
- timestamp?: string | number | Date
22
- }
23
- export interface UserAttrParams {
24
- [key: string]: string | number | Date | boolean
25
- }
26
- export type UserIdentifyParams = UserAttrParams & { id: string }
27
- type DataParameters = {
28
- [key: string]: string | number | Date | boolean
29
- } & {
30
- meta?: {
31
- [key: string]: string | number | Date | boolean
32
- }
33
- }
34
- type Methods = 'POST' | 'PUT' | 'DELETE' | 'GET'
35
- // type UserIdentifyParams = {
36
- // id: string
37
- // [key: string]: string | number | Date | boolean
38
- // }
39
- // type UserAttrParams = Omit<UserIdentifyParams, 'id'>
40
-
41
- // const rootURL = 'https://api.engage.so/v1'
42
- let auth: string = ''
43
- let currentUserId: string = ''
44
- const notMeta = ['created_at', 'is_account', 'number', 'device_token', 'device_platform', 'email', 'first_name', 'last_name', 'tz', 'app_version', 'app_build', 'app_last_active']
45
- const apiRoot = 'https://api.engage.so/v1'
46
-
47
- function isNull (v: any | null | undefined): boolean {
48
- return (v === null || v === undefined)
49
- }
50
- function isNullString (v: string | null | undefined): boolean {
51
- return (v === null || v === undefined || v === '')
52
- }
53
-
54
- function resolveUserId (uid?: string): string {
55
- if (!isNullString(uid)) {
56
- return uid as string
57
- }
58
- if (isNullString(currentUserId)) {
59
- throw new EngageError('User ID missing. Call identify() first or provide a uid parameter.')
60
- }
61
- return currentUserId
62
- }
63
-
64
- async function _request (url: string, params: Record<string, any> | null | undefined, method: Methods): Promise<object> {
65
- try {
66
- const o: any = {
67
- method,
68
- headers: {
69
- 'Content-Type': 'application/json;charset=utf-8',
70
- Authorization: `Basic ${auth}`
71
- }
72
- // throwHttpErrors: false,
73
- // prefixUrl: rootURL
74
- }
75
- if (!isNull(params)) {
76
- o.body = JSON.stringify(params)
77
- }
78
- // const response = await ky(url, o)
79
- const response = await fetch(`${apiRoot}${url}`, o)
80
- const body: any = await response.json()
81
- let error = 'API connection error'
82
- if (!response.ok) {
83
- if (typeof body?.error === 'string') {
84
- error = body.error
85
- }
86
- return { error }
87
- }
88
- return body
89
- } catch (e) {
90
- return { error: 'API connection error' }
91
- }
92
- }
93
- // Alias of _request method
94
- // Same with _request for now but can later have modifications
95
- export async function request (url: string, params: Record<string, any> | null | undefined, method: Methods = 'GET'): Promise<object> {
96
- return await _request(url, params, method)
97
- }
98
-
99
- export function init (key: Key | string): void {
100
- if (isNull(key)) {
101
- throw new EngageError('You need to pass in your API key(s).')
102
- }
103
-
104
- // Clear any stored user ID when reinitializing
105
- currentUserId = ''
106
-
107
- const options: Key = {
108
- key: '',
109
- secret: ''
110
- }
111
- if (typeof key === 'string') {
112
- if (key === '') {
113
- throw new EngageError('`key` is empty.')
114
- }
115
- options.key = key
116
- } else {
117
- if (isNullString(key.key)) {
118
- throw new EngageError('`key` missing in object.')
119
- }
120
- options.key = key.key
121
- if (!isNullString(key.secret)) {
122
- options.secret = key.secret
123
- }
124
- }
125
- // Set auth
126
- auth = btoa(`${options.key ?? ''}:${options.secret ?? ''}`)
127
- }
128
-
129
- // Data tracking
130
- export async function identify (user: UserIdentifyParams): Promise<object> {
131
- if (isNull(user)) {
132
- throw new EngageError('You need to pass an object with at least an id.')
133
- }
134
- if (isNullString(user.id)) {
135
- throw new EngageError('ID missing.')
136
- }
137
- if (!isNull(user.email) && (typeof user.email !== 'string' || !/^\S+@\S+$/.test(user.email))) {
138
- throw new EngageError('Email invalid.')
139
- }
140
- const params: DataParameters = {}
141
- params.meta = {}
142
- for (const k in user) {
143
- if (k === 'id' || notMeta.includes(k)) {
144
- params[k] = user[k]
145
- } else {
146
- params.meta[k] = user[k]
147
- }
148
- }
149
-
150
- // Store the user ID for use in other functions
151
- currentUserId = user.id
152
-
153
- return await _request(`/users/${user.id}`, params, 'PUT')
154
- }
155
-
156
- // Overloaded function signatures for addAttribute
157
- export async function addAttribute (attributes: UserAttrParams): Promise<object>
158
- export async function addAttribute (uid: string, attributes: UserAttrParams): Promise<object>
159
- export async function addAttribute (uidOrAttributes: string | UserAttrParams, attributes?: UserAttrParams): Promise<object> {
160
- let uid: string
161
- let attrs: UserAttrParams
162
-
163
- // Handle overloaded parameters
164
- if (typeof uidOrAttributes === 'string') {
165
- uid = resolveUserId(uidOrAttributes)
166
- if (isNull(attributes)) {
167
- throw new EngageError('Attributes missing when uid is provided.')
168
- }
169
- attrs = attributes as UserAttrParams
170
- } else {
171
- uid = resolveUserId()
172
- attrs = uidOrAttributes
173
- }
174
-
175
- if (isNull(attrs)) {
176
- throw new EngageError('Attributes missing.')
177
- }
178
- if (Object.keys(attrs).length === 0) {
179
- throw new EngageError('Attributes missing.')
180
- }
181
- const params: DataParameters = {}
182
- params.meta = {}
183
- for (const k in attrs) {
184
- if (notMeta.includes(k)) {
185
- params[k] = attrs[k]
186
- } else {
187
- params.meta[k] = attrs[k]
188
- }
189
- }
190
- if (Object.keys(params.meta).length === 0) {
191
- delete params.meta
192
- }
193
-
194
- return await _request(`/users/${uid}`, params, 'PUT')
195
- }
196
-
197
- // Overloaded function signatures for track
198
- export async function track (data: EventParameter): Promise<object>
199
- export async function track (uid: string, data: EventParameter): Promise<object>
200
- export async function track (uidOrData: string | EventParameter, data?: EventParameter): Promise<object> {
201
- let uid: string
202
- let eventData: EventParameter
203
-
204
- // Handle overloaded parameters
205
- if (typeof uidOrData === 'string') {
206
- uid = resolveUserId(uidOrData)
207
- if (isNull(data)) {
208
- throw new EngageError('Event data missing when uid is provided.')
209
- }
210
- eventData = data as EventParameter
211
- } else {
212
- uid = resolveUserId()
213
- eventData = uidOrData
214
- }
215
-
216
- if (isNull(eventData)) {
217
- throw new EngageError('Event data missing.')
218
- }
219
- if (typeof eventData === 'string') {
220
- eventData = {
221
- event: eventData,
222
- value: true
223
- }
224
- } else {
225
- if (Object.keys(eventData).length === 0) {
226
- throw new EngageError('Attributes missing.')
227
- }
228
- }
229
-
230
- return await _request(`/users/${uid}/events`, eventData, 'POST')
231
- }
232
-
233
- // Overloaded function signatures for merge
234
- export async function merge (destinationUid: string): Promise<object>
235
- export async function merge (sourceUid: string, destinationUid: string): Promise<object>
236
- export async function merge (sourceOrDestinationUid: string, destinationUid?: string): Promise<object> {
237
- let sourceUid: string
238
- let destUid: string
239
-
240
- // Handle overloaded parameters
241
- if (isNullString(destinationUid)) {
242
- // Called with one parameter: merge(destinationUid)
243
- sourceUid = resolveUserId()
244
- destUid = sourceOrDestinationUid
245
- } else {
246
- // Called with two parameters: merge(sourceUid, destinationUid)
247
- sourceUid = resolveUserId(sourceOrDestinationUid)
248
- destUid = destinationUid as string
249
- }
250
-
251
- if (isNullString(destUid)) {
252
- throw new EngageError('Destination ID missing.')
253
- }
254
-
255
- return await _request('/users/merge', {
256
- source: sourceUid,
257
- destination: destUid
258
- }, 'POST')
259
- }
260
-
261
- // Account functions
262
- export async function addToAccount (uid: string, accountId: string, role?: string): Promise<object> {
263
- if (isNullString(uid)) {
264
- throw new EngageError('User ID missing.')
265
- }
266
- if (isNullString(accountId)) {
267
- throw new EngageError('Account ID missing.')
268
- }
269
- if (!isNull(role) && typeof role !== 'string') {
270
- throw new EngageError('Role should be a text.')
271
- }
272
- const g: Record<string, string> = {
273
- id: accountId
274
- }
275
- if (!isNullString(role)) {
276
- g.role = role as string
277
- }
278
- return await _request(`/users/${uid}/accounts`, { accounts: [g] }, 'POST')
279
- }
280
-
281
- // Overloaded function signatures for removeFromAccount
282
- export async function removeFromAccount (accountId: string): Promise<object>
283
- export async function removeFromAccount (uid: string, accountId: string): Promise<object>
284
- export async function removeFromAccount (uidOrAccountId: string, accountId?: string): Promise<object> {
285
- let uid: string
286
- let acctId: string
287
-
288
- // Handle overloaded parameters
289
- if (isNullString(accountId)) {
290
- // Called with one parameter: removeFromAccount(accountId)
291
- uid = resolveUserId()
292
- acctId = uidOrAccountId
293
- } else {
294
- // Called with two parameters: removeFromAccount(uid, accountId)
295
- uid = resolveUserId(uidOrAccountId)
296
- acctId = accountId as string
297
- }
298
-
299
- if (isNullString(acctId)) {
300
- throw new EngageError('Account ID missing.')
301
- }
302
- return await _request(`/users/${uid}/accounts/${acctId}`, null, 'DELETE')
303
- }
304
-
305
- // Overloaded function signatures for changeAccountRole
306
- export async function changeAccountRole (accountId: string, role: string): Promise<object>
307
- export async function changeAccountRole (uid: string, accountId: string, role: string): Promise<object>
308
- export async function changeAccountRole (uidOrAccountId: string, accountIdOrRole: string, role?: string): Promise<object> {
309
- let uid: string
310
- let accountId: string
311
- let newRole: string
312
-
313
- // Handle overloaded parameters
314
- if (isNullString(role)) {
315
- // Called with two parameters: changeAccountRole(accountId, role)
316
- uid = resolveUserId()
317
- accountId = uidOrAccountId
318
- newRole = accountIdOrRole
319
- } else {
320
- // Called with three parameters: changeAccountRole(uid, accountId, role)
321
- uid = resolveUserId(uidOrAccountId)
322
- accountId = accountIdOrRole
323
- newRole = role as string
324
- }
325
-
326
- if (isNullString(accountId)) {
327
- throw new EngageError('Account ID missing.')
328
- }
329
- if (isNullString(newRole)) {
330
- throw new EngageError('New role missing.')
331
- }
332
- return await _request(`/users/${uid}/accounts/${accountId}`, { role: newRole }, 'PUT')
333
- }
334
-
335
- // Overloaded function signatures for convertToCustomer
336
- export async function convertToCustomer (): Promise<object>
337
- export async function convertToCustomer (uid?: string): Promise<object> {
338
- const userId = resolveUserId(uid)
339
- return await _request(`/users/${userId}/convert`, { type: 'customer' }, 'POST')
340
- }
341
-
342
- // Overloaded function signatures for convertToAccount
343
- export async function convertToAccount (): Promise<object>
344
- export async function convertToAccount (uid?: string): Promise<object> {
345
- const userId = resolveUserId(uid)
346
- return await _request(`/users/${userId}/convert`, { type: 'account' }, 'POST')
347
- }
348
-
349
- // Create an object containing all exports for easy access
350
- const EngageSDK = {
351
- init,
352
- identify,
353
- addAttribute,
354
- track,
355
- merge,
356
- addToAccount,
357
- removeFromAccount,
358
- changeAccountRole,
359
- convertToCustomer,
360
- convertToAccount,
361
- request
362
- }
363
-
364
- // Export as default for import EngageSDK syntax
365
- export default EngageSDK
@@ -1,231 +0,0 @@
1
- import * as Engage from '../src/index'
2
- const id = 'uzl38704@omeie.com'
3
- const sid = 'a6236e83-f7c1-4003-8e1a-951f6e1d5c60'
4
- const gid = '7d1a5e27-5a6b-4b38-a8bd-e59b8a1b2f41'
5
-
6
- describe('Init', () => {
7
- test('should throw if no parameters sent', () => {
8
- expect(() => {
9
- Engage.init()
10
- }).toThrow('API')
11
- })
12
- test('should throw if parameter is empty object', () => {
13
- expect(() => {
14
- Engage.init({})
15
- }).toThrow('key')
16
- })
17
- test('should throw if empty key sent', () => {
18
- expect(() => {
19
- Engage.init('')
20
- }).toThrow()
21
- })
22
- test('should not throw if key/secret is sent', () => {
23
- expect(() => {
24
- Engage.init({
25
- key: process.env.KEY,
26
- secret: process.env.SECRET
27
- })
28
- }).not.toThrow()
29
- })
30
- })
31
-
32
- describe('Add attribute', () => {
33
- test('should throw if no parameter passed', async () => {
34
- await expect(Engage.addAttribute()).rejects.toThrow(/id/i)
35
- })
36
- test('should throw if no data attribute passed', async () => {
37
- await expect(Engage.addAttribute(id)).rejects.toThrow(/attributes/i)
38
- })
39
- test('should throw if empty object passed', async () => {
40
- await expect(Engage.addAttribute(id, {})).rejects.toThrow(/attributes/i)
41
- })
42
- test('should resolve if parameters passed', async () => {
43
- await expect(Engage.addAttribute(sid, {
44
- first_name: 'Opeyemi',
45
- active: true,
46
- created_at: '2020-08-11'
47
- })).resolves.toMatchObject({
48
- uid: sid,
49
- first_name: 'Opeyemi'
50
- })
51
- })
52
- test('should turn to account if has is_account', async () => {
53
- await expect(Engage.addAttribute(id, {
54
- is_account: true
55
- })).resolves.toMatchObject({
56
- is_account: true
57
- })
58
- })
59
- test('should turn to user if is_account is not added', async () => {
60
- await expect(Engage.addAttribute(id, {
61
- is_account: false
62
- })).resolves.toMatchObject({
63
- is_account: false
64
- })
65
- })
66
- })
67
-
68
- describe('Track', () => {
69
- test('should throw if no parameter passed', async () => {
70
- await expect(Engage.track()).rejects.toThrow(/id/i)
71
- })
72
- test('should throw if no data attribute passed', async () => {
73
- await expect(Engage.track(id)).rejects.toThrow(/data/i)
74
- })
75
- test('should throw if empty object passed', async () => {
76
- await expect(Engage.track(id, {})).rejects.toThrow(/attributes/i)
77
- })
78
- test('should pass if string property passed', async () => {
79
- await expect(Engage.track(id, 'loggedin')).resolves.toMatchObject({
80
- status: 'ok'
81
- })
82
- })
83
- test('should pass if right parameters passed', async () => {
84
- await expect(Engage.track(id, {
85
- event: 'played',
86
- value: 'vid_133',
87
- timestamp: '2020-05-30T09:30:10Z'
88
- })).resolves.toMatchObject({
89
- status: 'ok'
90
- })
91
- })
92
- test('should resolve if other variant passed', async () => {
93
- await expect(Engage.track(id, {
94
- event: 'loggedin',
95
- properties: {
96
- ip: '127.0.0.1',
97
- referral: 'localhost'
98
- }
99
- })).resolves.toMatchObject({
100
- status: 'ok'
101
- })
102
- })
103
- })
104
-
105
- describe('Merge users', () => {
106
- test('should throw if no source id', async () => {
107
- await expect(Engage.merge()).rejects.toThrow(/id/i)
108
- })
109
- test('should throw if no destination id', async () => {
110
- await expect(Engage.merge(id)).rejects.toThrow(/id/i)
111
- })
112
- test('should merge if all is well', async () => {
113
- await expect(Engage.merge(sid, id)).resolves.toMatchObject({
114
- status: 'queued'
115
- })
116
- })
117
- })
118
-
119
- describe('Convert to account', () => {
120
- test('should convert to account', async () => {
121
- await expect(Engage.convertToAccount(gid)).resolves.toMatchObject({
122
- is_account: true
123
- })
124
- })
125
- })
126
-
127
- describe('Convert to customer', () => {
128
- test('should convert to customer', async () => {
129
- await expect(Engage.convertToCustomer(gid)).resolves.toMatchObject({
130
- is_account: false
131
- })
132
- })
133
- })
134
-
135
- describe('Add to account', () => {
136
- test('should throw if no account id added', async () => {
137
- await expect(Engage.addToAccount(id)).rejects.toThrow(/id/i)
138
- })
139
- test('should throw if role and role not string', async () => {
140
- await expect(Engage.addToAccount(id, gid, [ 'something' ])).rejects.toThrow(/Role/)
141
- })
142
- test('should pass if account id added', async () => {
143
- await expect(Engage.addToAccount(id, gid)).resolves.toMatchObject({
144
- accounts: [{
145
- id: gid
146
- }]
147
- })
148
- })
149
- })
150
-
151
- describe('Change account role', () => {
152
- test('should throw if no parameters', async () => {
153
- await expect(Engage.changeAccountRole()).rejects.toThrow(/missing/)
154
- })
155
- test('should throw if no account id', async () => {
156
- await expect(Engage.changeAccountRole(id)).rejects.toThrow(/missing/)
157
- })
158
- test('should throw if no role', async () => {
159
- await expect(Engage.changeAccountRole(id, gid)).rejects.toThrow(/missing/)
160
- })
161
- test('should pass if all parameters set', async () => {
162
- await expect(Engage.changeAccountRole(id, gid, 'owner')).resolves.toMatchObject({
163
- accounts: [{
164
- id: gid,
165
- role: 'owner'
166
- }]
167
- })
168
- })
169
- })
170
-
171
- describe('Remove from account', () => {
172
- test('should throw if no parameters', async () => {
173
- await expect(Engage.removeFromAccount()).rejects.toThrow(/missing/)
174
- })
175
- test('should throw if no account id', async () => {
176
- await expect(Engage.removeFromAccount(id)).rejects.toThrow(/missing/)
177
- })
178
- test('should pass if all parameters set', async () => {
179
- await expect(Engage.removeFromAccount(id, gid)).resolves.toMatchObject({
180
- accounts: []
181
- })
182
- })
183
- })
184
-
185
- describe('Identify', () => {
186
- test('should throw if no parameter passed', async () => {
187
- await expect(Engage.identify()).rejects.toThrow('object')
188
- })
189
- test('should throw if empty string passed', async () => {
190
- await expect(Engage.identify('')).rejects.toThrow('ID')
191
- })
192
- test('should throw if empty object passed', async () => {
193
- await expect(Engage.identify({})).rejects.toThrow('ID')
194
- })
195
- test('should not throw if no email passed', async () => {
196
- await expect(() => {
197
- Engage.identify({ id: gid })
198
- }).not.toThrow()
199
- })
200
- test('should throw if invalid email passed', async () => {
201
- await expect(Engage.identify({
202
- id,
203
- email: 'invalid'
204
- })).rejects.toThrow(/email/i)
205
- })
206
- test('should work if id and email passed', async () => {
207
- await expect(Engage.identify({
208
- id,
209
- email: 'fickledreams@yahoo.com'
210
- })).resolves.toMatchObject({
211
- uid: id,
212
- email: 'fickledreams@yahoo.com'
213
- })
214
- })
215
- test('should turn to account if has is_account', async () => {
216
- await expect(Engage.identify({
217
- id,
218
- is_account: true
219
- })).resolves.toMatchObject({
220
- is_account: true
221
- })
222
- })
223
- test('should turn to user if is_account is false', async () => {
224
- await expect(Engage.identify({
225
- id,
226
- is_account: false
227
- })).resolves.toMatchObject({
228
- is_account: false
229
- })
230
- })
231
- })
package/tests/oop.test.ts DELETED
@@ -1,41 +0,0 @@
1
- import * as Engage from '../src/index'
2
- import EngageSDK from '../src/index'
3
-
4
- describe('Object-Oriented Pattern', () => {
5
- describe('Functional style imports', () => {
6
- test('should work with named import init', () => {
7
- expect(() => {
8
- Engage.init('test-key')
9
- }).not.toThrow()
10
- })
11
-
12
- test('should work with destructured init', () => {
13
- const { init } = Engage
14
- expect(() => {
15
- init('test-key-2')
16
- }).not.toThrow()
17
- })
18
- })
19
-
20
- describe('Static class style imports', () => {
21
- test('should work with default import init', () => {
22
- expect(() => {
23
- EngageSDK.init('test-key')
24
- }).not.toThrow()
25
- })
26
-
27
- test('should have all expected methods on default export', () => {
28
- expect(typeof EngageSDK.init).toBe('function')
29
- expect(typeof EngageSDK.identify).toBe('function')
30
- expect(typeof EngageSDK.addAttribute).toBe('function')
31
- expect(typeof EngageSDK.track).toBe('function')
32
- expect(typeof EngageSDK.merge).toBe('function')
33
- expect(typeof EngageSDK.addToAccount).toBe('function')
34
- expect(typeof EngageSDK.removeFromAccount).toBe('function')
35
- expect(typeof EngageSDK.changeAccountRole).toBe('function')
36
- expect(typeof EngageSDK.convertToCustomer).toBe('function')
37
- expect(typeof EngageSDK.convertToAccount).toBe('function')
38
- expect(typeof EngageSDK.request).toBe('function')
39
- })
40
- })
41
- })