@engage_so/core 1.7.0 → 2.0.0
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/.eslintignore +3 -0
- package/jest.config.js +7 -0
- package/package.json +17 -10
- package/src/error.ts +6 -0
- package/src/index.ts +242 -0
- package/tests/index.test.ts +231 -0
- package/tsconfig.json +18 -0
- package/error.js +0 -8
- package/index.js +0 -216
package/.eslintignore
ADDED
package/jest.config.js
ADDED
package/package.json
CHANGED
@@ -1,22 +1,29 @@
|
|
1
1
|
{
|
2
2
|
"name": "@engage_so/core",
|
3
|
-
"version": "
|
4
|
-
"description": "
|
5
|
-
"main": "index.js",
|
3
|
+
"version": "2.0.0",
|
4
|
+
"description": "Engage JS core.",
|
5
|
+
"main": "./dist/index.js",
|
6
|
+
"types": "./dist/index.d.ts",
|
6
7
|
"scripts": {
|
7
|
-
"test": "jest"
|
8
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
9
|
+
"build": "tsc -p tsconfig.json",
|
10
|
+
"prepublishOnly": "npm run build"
|
8
11
|
},
|
9
12
|
"publishConfig": {
|
10
13
|
"access": "public"
|
11
14
|
},
|
12
15
|
"author": "Engage",
|
13
16
|
"license": "MIT",
|
14
|
-
"dependencies": {
|
15
|
-
"buffer": "^6.0.3",
|
16
|
-
"cross-fetch": "^3.1.5"
|
17
|
-
},
|
18
17
|
"devDependencies": {
|
19
|
-
"
|
18
|
+
"@tsconfig/recommended": "^1.0.3",
|
19
|
+
"@types/jest": "^29.5.12",
|
20
|
+
"@types/node": "^20.11.19",
|
21
|
+
"jest": "^29.7.0",
|
22
|
+
"ts-jest": "^29.1.2",
|
23
|
+
"ts-standard": "^12.0.2"
|
24
|
+
},
|
25
|
+
"dependencies": {
|
26
|
+
"cross-fetch": "^4.0.0"
|
20
27
|
},
|
21
|
-
"gitHead": "
|
28
|
+
"gitHead": "ecc6ef9a3277da36b56fb4169dbfafbdde9b926b"
|
22
29
|
}
|
package/src/error.ts
ADDED
package/src/index.ts
ADDED
@@ -0,0 +1,242 @@
|
|
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
|
+
interface Key {
|
12
|
+
key?: string
|
13
|
+
secret?: string
|
14
|
+
}
|
15
|
+
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
|
+
type UserAttrParams = {
|
24
|
+
[key: string]: string | number | Date | boolean
|
25
|
+
}
|
26
|
+
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'
|
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
|
+
const notMeta = ['created_at', 'is_account', 'number', 'device_token', 'device_platform', 'email', 'first_name', 'last_name', 'tz', 'app_version', 'app_build', 'app_last_active']
|
44
|
+
const apiRoot = 'https://api.engage.so/'
|
45
|
+
|
46
|
+
async function _request (url: string, params: Record<string, any> | null, method: Methods) {
|
47
|
+
try {
|
48
|
+
const o: any = {
|
49
|
+
method,
|
50
|
+
headers: {
|
51
|
+
'Content-Type': 'application/json;charset=utf-8',
|
52
|
+
Authorization: `Basic ${auth}`
|
53
|
+
},
|
54
|
+
// throwHttpErrors: false,
|
55
|
+
// prefixUrl: rootURL
|
56
|
+
}
|
57
|
+
if (params) {
|
58
|
+
o.body = JSON.stringify(params)
|
59
|
+
}
|
60
|
+
// const response = await ky(url, o)
|
61
|
+
const response = await fetch(`${apiRoot}${url}`, o)
|
62
|
+
const body: any = await response.json()
|
63
|
+
let error = 'API connection error'
|
64
|
+
if (!response.ok) {
|
65
|
+
if (body && body.error) {
|
66
|
+
error = body.error
|
67
|
+
}
|
68
|
+
return { error }
|
69
|
+
}
|
70
|
+
return body
|
71
|
+
} catch (e) {
|
72
|
+
return { error: 'API connection error' }
|
73
|
+
}
|
74
|
+
}
|
75
|
+
// Alias of _request method
|
76
|
+
// Same with _request for now but can later have modifications
|
77
|
+
export function request (url: string, params: Record<string, any> | null, method: Methods): object {
|
78
|
+
return _request(url, params, method)
|
79
|
+
}
|
80
|
+
|
81
|
+
export function init (key: Key | string) {
|
82
|
+
if (!key) {
|
83
|
+
throw new EngageError('You need to pass in your API key(s).')
|
84
|
+
}
|
85
|
+
const options: Key = {
|
86
|
+
key: undefined,
|
87
|
+
secret: ''
|
88
|
+
}
|
89
|
+
if (typeof key === 'string') {
|
90
|
+
options.key = key
|
91
|
+
} else {
|
92
|
+
if (!key.key) {
|
93
|
+
throw new EngageError('`key` missing in object.')
|
94
|
+
}
|
95
|
+
if (key.key) {
|
96
|
+
options.key = `${key.key}`
|
97
|
+
}
|
98
|
+
if (key.secret) {
|
99
|
+
options.secret = `${key.secret}`
|
100
|
+
}
|
101
|
+
}
|
102
|
+
// Set auth
|
103
|
+
// auth = Buffer.from(`${options.key}:${options.secret}`).toString('base64')
|
104
|
+
auth = btoa(`${options.key}:${options.secret}`)
|
105
|
+
}
|
106
|
+
|
107
|
+
// Data tracking
|
108
|
+
export async function identify (user: UserIdentifyParams) {
|
109
|
+
if (!user) {
|
110
|
+
throw new EngageError('You need to pass an object with at least an id.')
|
111
|
+
}
|
112
|
+
if (!user.id) {
|
113
|
+
throw new EngageError('ID missing.')
|
114
|
+
}
|
115
|
+
if (user.email && (typeof user.email !== 'string' || !/^\S+@\S+$/.test(user.email))) {
|
116
|
+
throw new EngageError('Email invalid.')
|
117
|
+
}
|
118
|
+
const params: DataParameters = {}
|
119
|
+
params.meta = {}
|
120
|
+
for (const k in user) {
|
121
|
+
if (k === 'id' || notMeta.includes(k)) {
|
122
|
+
params[k] = user[k]
|
123
|
+
} else {
|
124
|
+
params.meta[k] = user[k]
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
return _request(`users/${user.id}`, params, 'PUT')
|
129
|
+
}
|
130
|
+
export async function addAttribute (uid: string, attributes: UserAttrParams) {
|
131
|
+
if (!uid) {
|
132
|
+
throw new EngageError('User ID missing.')
|
133
|
+
}
|
134
|
+
if (!attributes) {
|
135
|
+
throw new EngageError('Attributes missing.')
|
136
|
+
}
|
137
|
+
if (!Object.keys(attributes).length) {
|
138
|
+
throw new EngageError('Attributes missing.')
|
139
|
+
}
|
140
|
+
const params: DataParameters = {}
|
141
|
+
params.meta = {}
|
142
|
+
for (const k in attributes) {
|
143
|
+
if (notMeta.includes(k)) {
|
144
|
+
params[k] = attributes[k]
|
145
|
+
} else {
|
146
|
+
params.meta[k] = attributes[k]
|
147
|
+
Object.assign(params.meta, k, attributes[k])
|
148
|
+
}
|
149
|
+
}
|
150
|
+
if (Object.keys(params.meta).length) {
|
151
|
+
delete params.meta
|
152
|
+
}
|
153
|
+
|
154
|
+
return _request(`users/${uid}`, params, 'PUT')
|
155
|
+
}
|
156
|
+
export async function track (uid: string, data: EventParameter) {
|
157
|
+
if (!uid) {
|
158
|
+
throw new EngageError('User ID missing.')
|
159
|
+
}
|
160
|
+
if (!data) {
|
161
|
+
throw new EngageError('Event data missing.')
|
162
|
+
}
|
163
|
+
if (typeof data === 'string') {
|
164
|
+
data = {
|
165
|
+
event: data,
|
166
|
+
value: true
|
167
|
+
}
|
168
|
+
} else {
|
169
|
+
if (!Object.keys(data).length) {
|
170
|
+
throw new EngageError('Attributes missing.')
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
return _request(`users/${uid}/events`, data, 'POST')
|
175
|
+
}
|
176
|
+
|
177
|
+
export async function merge (sourceUid: string, destinationUid: string) {
|
178
|
+
if (!sourceUid) {
|
179
|
+
throw new EngageError('Source ID missing.')
|
180
|
+
}
|
181
|
+
if (!destinationUid) {
|
182
|
+
throw new EngageError('Destination ID missing.')
|
183
|
+
}
|
184
|
+
|
185
|
+
return _request(`users/merge`, {
|
186
|
+
source: sourceUid,
|
187
|
+
destination: destinationUid
|
188
|
+
}, 'POST')
|
189
|
+
}
|
190
|
+
|
191
|
+
// Account functions
|
192
|
+
export async function addToAccount(uid: string, accountId: string, role: string) {
|
193
|
+
if (!uid) {
|
194
|
+
throw new EngageError('User ID missing.')
|
195
|
+
}
|
196
|
+
if (!accountId) {
|
197
|
+
throw new EngageError('Account ID missing.')
|
198
|
+
}
|
199
|
+
if (role && typeof role !== 'string') {
|
200
|
+
throw new EngageError('Role should be a text.')
|
201
|
+
}
|
202
|
+
const g: Record<string, string> = {
|
203
|
+
id: accountId
|
204
|
+
}
|
205
|
+
if (role) {
|
206
|
+
g.role = role
|
207
|
+
}
|
208
|
+
return _request(`users/${uid}/accounts`, { accounts: [g] }, 'POST')
|
209
|
+
}
|
210
|
+
export async function removeFromAccount (uid: string, accountId: string){
|
211
|
+
if (!uid) {
|
212
|
+
throw new EngageError('User ID missing.')
|
213
|
+
}
|
214
|
+
if (!accountId) {
|
215
|
+
throw new EngageError('Account ID missing.')
|
216
|
+
}
|
217
|
+
return _request(`users/${uid}/accounts/${accountId}`, null, 'DELETE')
|
218
|
+
}
|
219
|
+
export async function changeAccountRole (uid: string, accountId: string, role: string) {
|
220
|
+
if (!uid) {
|
221
|
+
throw new EngageError('User ID missing.')
|
222
|
+
}
|
223
|
+
if (!accountId) {
|
224
|
+
throw new EngageError('Account ID missing.')
|
225
|
+
}
|
226
|
+
if (!role) {
|
227
|
+
throw new EngageError('New role missing.')
|
228
|
+
}
|
229
|
+
return _request(`users/${uid}/accounts/${accountId}`, { role }, 'PUT')
|
230
|
+
}
|
231
|
+
export async function convertToCustomer (uid: string) {
|
232
|
+
if (!uid) {
|
233
|
+
throw new EngageError('User ID missing.')
|
234
|
+
}
|
235
|
+
return _request(`users/${uid}/convert`, { type: 'customer' }, 'POST')
|
236
|
+
}
|
237
|
+
export async function convertToAccount (uid: string) {
|
238
|
+
if (!uid) {
|
239
|
+
throw new EngageError('User ID missing.')
|
240
|
+
}
|
241
|
+
return _request(`users/${uid}/convert`, { type: 'account' }, 'POST')
|
242
|
+
}
|
@@ -0,0 +1,231 @@
|
|
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('Identify', () => {
|
33
|
+
test('should throw if no parameter passed', async () => {
|
34
|
+
await expect(Engage.identify()).rejects.toThrow('object')
|
35
|
+
})
|
36
|
+
test('should throw if empty string passed', async () => {
|
37
|
+
await expect(Engage.identify('')).rejects.toThrow('id')
|
38
|
+
})
|
39
|
+
test('should throw if empty object passed', async () => {
|
40
|
+
await expect(Engage.identify({})).rejects.toThrow(/id/i)
|
41
|
+
})
|
42
|
+
test('should not throw if no email passed', async () => {
|
43
|
+
await expect(() => {
|
44
|
+
Engage.identify({ id: gid })
|
45
|
+
}).not.toThrow()
|
46
|
+
})
|
47
|
+
test('should throw if invalid email passed', async () => {
|
48
|
+
await expect(Engage.identify({
|
49
|
+
id,
|
50
|
+
email: 'invalid'
|
51
|
+
})).rejects.toThrow(/email/i)
|
52
|
+
})
|
53
|
+
test('should work if id and email passed', async () => {
|
54
|
+
await expect(Engage.identify({
|
55
|
+
id,
|
56
|
+
email: 'fickledreams@yahoo.com'
|
57
|
+
})).resolves.toMatchObject({
|
58
|
+
uid: id,
|
59
|
+
email: 'fickledreams@yahoo.com'
|
60
|
+
})
|
61
|
+
})
|
62
|
+
test('should turn to account if has is_account', async () => {
|
63
|
+
await expect(Engage.identify({
|
64
|
+
id,
|
65
|
+
is_account: true
|
66
|
+
})).resolves.toMatchObject({
|
67
|
+
is_account: true
|
68
|
+
})
|
69
|
+
})
|
70
|
+
test('should turn to user if is_account is false', async () => {
|
71
|
+
await expect(Engage.identify({
|
72
|
+
id,
|
73
|
+
is_account: false
|
74
|
+
})).resolves.toMatchObject({
|
75
|
+
is_account: false
|
76
|
+
})
|
77
|
+
})
|
78
|
+
})
|
79
|
+
|
80
|
+
describe('Add attribute', () => {
|
81
|
+
test('should throw if no parameter passed', async () => {
|
82
|
+
await expect(Engage.addAttribute()).rejects.toThrow('id')
|
83
|
+
})
|
84
|
+
test('should throw if no data attribute passed', async () => {
|
85
|
+
await expect(Engage.addAttribute(id)).rejects.toThrow(/attributes/i)
|
86
|
+
})
|
87
|
+
test('should throw if empty object passed', async () => {
|
88
|
+
await expect(Engage.addAttribute(id, {})).rejects.toThrow(/attributes/i)
|
89
|
+
})
|
90
|
+
test('should resolve if parameters passed', async () => {
|
91
|
+
await expect(Engage.addAttribute(sid, {
|
92
|
+
first_name: 'Opeyemi',
|
93
|
+
active: true,
|
94
|
+
created_at: '2020-08-11'
|
95
|
+
})).resolves.toMatchObject({
|
96
|
+
uid: sid,
|
97
|
+
first_name: 'Opeyemi'
|
98
|
+
})
|
99
|
+
})
|
100
|
+
test('should turn to account if has is_account', async () => {
|
101
|
+
await expect(Engage.addAttribute(id, {
|
102
|
+
is_account: true
|
103
|
+
})).resolves.toMatchObject({
|
104
|
+
is_account: true
|
105
|
+
})
|
106
|
+
})
|
107
|
+
test('should turn to user if is_account is not added', async () => {
|
108
|
+
await expect(Engage.addAttribute(id, {
|
109
|
+
is_account: false
|
110
|
+
})).resolves.toMatchObject({
|
111
|
+
is_account: false
|
112
|
+
})
|
113
|
+
})
|
114
|
+
})
|
115
|
+
|
116
|
+
describe('Track', () => {
|
117
|
+
test('should throw if no parameter passed', async () => {
|
118
|
+
await expect(Engage.track()).rejects.toThrow('ID')
|
119
|
+
})
|
120
|
+
test('should throw if no data attribute passed', async () => {
|
121
|
+
await expect(Engage.track(id)).rejects.toThrow(/data/i)
|
122
|
+
})
|
123
|
+
test('should throw if empty object passed', async () => {
|
124
|
+
await expect(Engage.track(id, {})).rejects.toThrow(/attributes/i)
|
125
|
+
})
|
126
|
+
test('should pass if string property passed', async () => {
|
127
|
+
await expect(Engage.track(id, 'loggedin')).resolves.toMatchObject({
|
128
|
+
status: 'ok'
|
129
|
+
})
|
130
|
+
})
|
131
|
+
test('should pass if right parameters passed', async () => {
|
132
|
+
await expect(Engage.track(id, {
|
133
|
+
event: 'played',
|
134
|
+
value: 'vid_133',
|
135
|
+
timestamp: '2020-05-30T09:30:10Z'
|
136
|
+
})).resolves.toMatchObject({
|
137
|
+
status: 'ok'
|
138
|
+
})
|
139
|
+
})
|
140
|
+
test('should resolve if other variant passed', async () => {
|
141
|
+
await expect(Engage.track(id, {
|
142
|
+
event: 'loggedin',
|
143
|
+
properties: {
|
144
|
+
ip: '127.0.0.1',
|
145
|
+
referral: 'localhost'
|
146
|
+
}
|
147
|
+
})).resolves.toMatchObject({
|
148
|
+
status: 'ok'
|
149
|
+
})
|
150
|
+
})
|
151
|
+
})
|
152
|
+
|
153
|
+
describe('Merge users', () => {
|
154
|
+
test('should throw if no source id', async () => {
|
155
|
+
await expect(Engage.merge()).rejects.toThrow(/id/i)
|
156
|
+
})
|
157
|
+
test('should throw if no destination id', async () => {
|
158
|
+
await expect(Engage.merge(id)).rejects.toThrow(/id/i)
|
159
|
+
})
|
160
|
+
test('should merge if all is well', async () => {
|
161
|
+
await expect(Engage.merge(sid, id)).resolves.toMatchObject({
|
162
|
+
status: 'queued'
|
163
|
+
})
|
164
|
+
})
|
165
|
+
})
|
166
|
+
|
167
|
+
describe('Convert to account', () => {
|
168
|
+
test('should convert to account', async () => {
|
169
|
+
await expect(Engage.convertToAccount(gid)).resolves.toMatchObject({
|
170
|
+
is_account: true
|
171
|
+
})
|
172
|
+
})
|
173
|
+
})
|
174
|
+
|
175
|
+
describe('Convert to customer', () => {
|
176
|
+
test('should convert to customer', async () => {
|
177
|
+
await expect(Engage.convertToCustomer(gid)).resolves.toMatchObject({
|
178
|
+
is_account: false
|
179
|
+
})
|
180
|
+
})
|
181
|
+
})
|
182
|
+
|
183
|
+
describe('Add to account', () => {
|
184
|
+
test('should throw if no account id added', async () => {
|
185
|
+
await expect(Engage.addToAccount(id)).rejects.toThrow(/id/i)
|
186
|
+
})
|
187
|
+
test('should throw if role and role not string', async () => {
|
188
|
+
await expect(Engage.addToAccount(id, gid, [ 'something' ])).rejects.toThrow(/Role/)
|
189
|
+
})
|
190
|
+
test('should pass if account id added', async () => {
|
191
|
+
await expect(Engage.addToAccount(id, gid)).resolves.toMatchObject({
|
192
|
+
accounts: [{
|
193
|
+
id: gid
|
194
|
+
}]
|
195
|
+
})
|
196
|
+
})
|
197
|
+
})
|
198
|
+
|
199
|
+
describe('Change account role', () => {
|
200
|
+
test('should throw if no parameters', async () => {
|
201
|
+
await expect(Engage.changeAccountRole()).rejects.toThrow(/missing/)
|
202
|
+
})
|
203
|
+
test('should throw if no account id', async () => {
|
204
|
+
await expect(Engage.changeAccountRole(id)).rejects.toThrow(/missing/)
|
205
|
+
})
|
206
|
+
test('should throw if no role', async () => {
|
207
|
+
await expect(Engage.changeAccountRole(id, gid)).rejects.toThrow(/missing/)
|
208
|
+
})
|
209
|
+
test('should pass if all parameters set', async () => {
|
210
|
+
await expect(Engage.changeAccountRole(id, gid, 'owner')).resolves.toMatchObject({
|
211
|
+
accounts: [{
|
212
|
+
id: gid,
|
213
|
+
role: 'owner'
|
214
|
+
}]
|
215
|
+
})
|
216
|
+
})
|
217
|
+
})
|
218
|
+
|
219
|
+
describe('Remove from account', () => {
|
220
|
+
test('should throw if no parameters', async () => {
|
221
|
+
await expect(Engage.removeFromAccount()).rejects.toThrow(/missing/)
|
222
|
+
})
|
223
|
+
test('should throw if no account id', async () => {
|
224
|
+
await expect(Engage.removeFromAccount(id)).rejects.toThrow(/missing/)
|
225
|
+
})
|
226
|
+
test('should pass if all parameters set', async () => {
|
227
|
+
await expect(Engage.removeFromAccount(id, gid)).resolves.toMatchObject({
|
228
|
+
accounts: []
|
229
|
+
})
|
230
|
+
})
|
231
|
+
})
|
package/tsconfig.json
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2015",
|
4
|
+
"module": "NodeNext",
|
5
|
+
"declaration": true,
|
6
|
+
"declarationMap": true,
|
7
|
+
"outDir": "./dist",
|
8
|
+
"esModuleInterop": true,
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
10
|
+
"strict": true,
|
11
|
+
"skipLibCheck": true
|
12
|
+
},
|
13
|
+
"exclude": [
|
14
|
+
"node_modules",
|
15
|
+
"tests",
|
16
|
+
"dist"
|
17
|
+
]
|
18
|
+
}
|
package/error.js
DELETED
package/index.js
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
const fetch = require('cross-fetch')
|
2
|
-
const Buffer = require('buffer/').Buffer
|
3
|
-
const EngageError = require('./error')
|
4
|
-
const root = 'https://api.engage.so/v1'
|
5
|
-
if (typeof btoa === 'undefined') {
|
6
|
-
global.btoa = function (str) {
|
7
|
-
return Buffer.from(str).toString('base64')
|
8
|
-
}
|
9
|
-
}
|
10
|
-
|
11
|
-
const options = {
|
12
|
-
key: null,
|
13
|
-
secret: ''
|
14
|
-
}
|
15
|
-
|
16
|
-
async function _request (url, params, method) {
|
17
|
-
try {
|
18
|
-
const o = {
|
19
|
-
method,
|
20
|
-
headers: {
|
21
|
-
'Content-Type': 'application/json;charset=utf-8',
|
22
|
-
Authorization: `Basic ${btoa(`${options.key}:${options.secret}`)}`
|
23
|
-
},
|
24
|
-
}
|
25
|
-
if (params) {
|
26
|
-
o.body = JSON.stringify(params)
|
27
|
-
}
|
28
|
-
const response = await fetch(url, o)
|
29
|
-
const body = await response.json()
|
30
|
-
let error = 'API connection error'
|
31
|
-
if (!response.ok) {
|
32
|
-
if (body && body.error) {
|
33
|
-
error = body.error
|
34
|
-
}
|
35
|
-
return { error }
|
36
|
-
}
|
37
|
-
return body
|
38
|
-
} catch (e) {
|
39
|
-
// console.log(e)
|
40
|
-
return { error: 'API connection error' }
|
41
|
-
}
|
42
|
-
}
|
43
|
-
|
44
|
-
// Alias of _request method
|
45
|
-
// Same with _request for now but can later have modifications
|
46
|
-
const request = (url, params, method) => {
|
47
|
-
return _request(`${root}${url}`, params, method)
|
48
|
-
}
|
49
|
-
|
50
|
-
const init = (o) => {
|
51
|
-
if (!o) {
|
52
|
-
throw new EngageError('You need to pass in your API key.')
|
53
|
-
}
|
54
|
-
if (typeof o === 'string') {
|
55
|
-
options.key = o
|
56
|
-
return
|
57
|
-
}
|
58
|
-
|
59
|
-
if (!o.key) {
|
60
|
-
throw new EngageError('`key` missing in object.')
|
61
|
-
}
|
62
|
-
if (o.key) {
|
63
|
-
options.key = `${o.key}`
|
64
|
-
}
|
65
|
-
if (o.secret) {
|
66
|
-
options.secret = `${o.secret}`
|
67
|
-
}
|
68
|
-
}
|
69
|
-
|
70
|
-
const identify = async (o) => {
|
71
|
-
if (!o) {
|
72
|
-
throw new EngageError('You need to pass an object with at least an id.')
|
73
|
-
}
|
74
|
-
if (!o.id) {
|
75
|
-
throw new EngageError('ID missing.')
|
76
|
-
}
|
77
|
-
if (o.email && !/^\S+@\S+$/.test(o.email)) {
|
78
|
-
throw new EngageError('Email invalid.')
|
79
|
-
}
|
80
|
-
const allowed = ['id', 'is_account', 'email', 'number', 'created_at', 'device_token', 'device_platform', 'first_name', 'last_name', 'tz']
|
81
|
-
const params = {
|
82
|
-
meta: {}
|
83
|
-
}
|
84
|
-
for (const k in o) {
|
85
|
-
if (allowed.includes(k)) {
|
86
|
-
params[k] = o[k]
|
87
|
-
} else {
|
88
|
-
params.meta[k] = o[k]
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
return _request(`${root}/users/${o.id}`, params, 'PUT')
|
93
|
-
}
|
94
|
-
|
95
|
-
const addAttribute = async (uid, data) => {
|
96
|
-
if (!uid) {
|
97
|
-
throw new EngageError('User id missing.')
|
98
|
-
}
|
99
|
-
if (!data) {
|
100
|
-
throw new EngageError('Attributes missing.')
|
101
|
-
}
|
102
|
-
if (!Object.keys(data).length) {
|
103
|
-
throw new EngageError('Attributes missing.')
|
104
|
-
}
|
105
|
-
const notMeta = ['created_at', 'is_account', 'number', 'device_token', 'device_platform', 'email', 'first_name', 'last_name', 'tz', 'app_version', 'app_build', 'app_last_active']
|
106
|
-
const params = { meta: {} }
|
107
|
-
for (const k in data) {
|
108
|
-
if (notMeta.includes(k)) {
|
109
|
-
params[k] = data[k]
|
110
|
-
} else {
|
111
|
-
params.meta[k] = data[k]
|
112
|
-
}
|
113
|
-
}
|
114
|
-
|
115
|
-
return _request(`${root}/users/${uid}`, params, 'PUT')
|
116
|
-
}
|
117
|
-
|
118
|
-
const track = async (uid, data) => {
|
119
|
-
if (!uid) {
|
120
|
-
throw new EngageError('User id missing.')
|
121
|
-
}
|
122
|
-
if (!data) {
|
123
|
-
throw new EngageError('Attributes missing.')
|
124
|
-
}
|
125
|
-
if (typeof data === 'string') {
|
126
|
-
data = {
|
127
|
-
event: data,
|
128
|
-
value: true
|
129
|
-
}
|
130
|
-
} else {
|
131
|
-
if (!Object.keys(data).length) {
|
132
|
-
throw new EngageError('Attributes missing.')
|
133
|
-
}
|
134
|
-
}
|
135
|
-
|
136
|
-
return _request(`${root}/users/${uid}/events`, data, 'POST')
|
137
|
-
}
|
138
|
-
|
139
|
-
const merge = async (source, destination) => {
|
140
|
-
if (!source) {
|
141
|
-
throw new EngageError('Source ID missing.')
|
142
|
-
}
|
143
|
-
if (!destination) {
|
144
|
-
throw new EngageError('Destination ID missing.')
|
145
|
-
}
|
146
|
-
|
147
|
-
return _request(`${root}/users/merge`, { source, destination }, 'POST')
|
148
|
-
}
|
149
|
-
|
150
|
-
const addToAccount = async (uid, gid, role) => {
|
151
|
-
if (!uid) {
|
152
|
-
throw new EngageError('User id missing.')
|
153
|
-
}
|
154
|
-
if (!gid) {
|
155
|
-
throw new EngageError('Account id missing.')
|
156
|
-
}
|
157
|
-
if (role && typeof role !== 'string') {
|
158
|
-
throw new EngageError('Role should be a text.')
|
159
|
-
}
|
160
|
-
const g = {
|
161
|
-
id: gid
|
162
|
-
}
|
163
|
-
if (role) {
|
164
|
-
g.role = role
|
165
|
-
}
|
166
|
-
return _request(`${root}/users/${uid}/accounts`, { accounts: [g] }, 'POST')
|
167
|
-
}
|
168
|
-
const removeFromAccount = async (uid, gid) => {
|
169
|
-
if (!uid) {
|
170
|
-
throw new EngageError('User id missing.')
|
171
|
-
}
|
172
|
-
if (!gid) {
|
173
|
-
throw new EngageError('Account id missing.')
|
174
|
-
}
|
175
|
-
return _request(`${root}/users/${uid}/accounts/${gid}`, null, 'DELETE')
|
176
|
-
}
|
177
|
-
|
178
|
-
const changeAccountRole = async (uid, gid, role) => {
|
179
|
-
if (!uid) {
|
180
|
-
throw new EngageError('User id missing.')
|
181
|
-
}
|
182
|
-
if (!gid) {
|
183
|
-
throw new EngageError('Account id missing.')
|
184
|
-
}
|
185
|
-
if (!role) {
|
186
|
-
throw new EngageError('New role missing.')
|
187
|
-
}
|
188
|
-
return _request(`${root}/users/${uid}/accounts/${gid}`, { role }, 'PUT')
|
189
|
-
}
|
190
|
-
|
191
|
-
const convertToCustomer = async (uid) => {
|
192
|
-
if (!uid) {
|
193
|
-
throw new EngageError('User id missing.')
|
194
|
-
}
|
195
|
-
return _request(`${root}/users/${uid}/convert`, { type: 'customer' }, 'POST')
|
196
|
-
}
|
197
|
-
const convertToAccount = async (uid) => {
|
198
|
-
if (!uid) {
|
199
|
-
throw new EngageError('User id missing.')
|
200
|
-
}
|
201
|
-
return _request(`${root}/users/${uid}/convert`, { type: 'account' }, 'POST')
|
202
|
-
}
|
203
|
-
|
204
|
-
module.exports = {
|
205
|
-
init,
|
206
|
-
identify,
|
207
|
-
addAttribute,
|
208
|
-
track,
|
209
|
-
merge,
|
210
|
-
request,
|
211
|
-
addToAccount,
|
212
|
-
removeFromAccount,
|
213
|
-
changeAccountRole,
|
214
|
-
convertToCustomer,
|
215
|
-
convertToAccount
|
216
|
-
}
|