@tellescope/sdk 0.0.25 → 0.0.29
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/lib/cjs/enduser.d.ts +4 -8
- package/lib/cjs/enduser.d.ts.map +1 -1
- package/lib/cjs/enduser.js +7 -8
- package/lib/cjs/enduser.js.map +1 -1
- package/lib/cjs/sdk.d.ts +4 -8
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +2 -26
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/session.d.ts +2 -2
- package/lib/cjs/session.d.ts.map +1 -1
- package/lib/cjs/session.js +13 -21
- package/lib/cjs/session.js.map +1 -1
- package/lib/cjs/tests/tests.js +79 -48
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/enduser.d.ts +5 -8
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/enduser.js +7 -8
- package/lib/esm/enduser.js.map +1 -1
- package/lib/esm/sdk.d.ts +5 -8
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +2 -26
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/session.d.ts +3 -2
- package/lib/esm/session.d.ts.map +1 -1
- package/lib/esm/session.js +13 -21
- package/lib/esm/session.js.map +1 -1
- package/lib/esm/tests/tests.js +74 -47
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/enduser.ts +8 -8
- package/src/sdk.ts +5 -16
- package/src/session.ts +12 -17
- package/src/tests/tests.ts +26 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"description": "Code for interacting with the Tellescope API",
|
|
5
5
|
"main": "./lib/cjs/sdk.js",
|
|
6
6
|
"module": "./lib/esm/sdk.js",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/tellescope-os/tellescope#readme",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@tellescope/constants": "^0.0.
|
|
36
|
-
"@tellescope/schema": "^0.0.
|
|
37
|
-
"@tellescope/testing": "^0.0.
|
|
38
|
-
"@tellescope/types-client": "^0.0.
|
|
39
|
-
"@tellescope/types-models": "^0.0.
|
|
40
|
-
"@tellescope/types-utilities": "^0.0.
|
|
41
|
-
"@tellescope/utilities": "^0.0.
|
|
42
|
-
"@tellescope/validation": "^0.0.
|
|
35
|
+
"@tellescope/constants": "^0.0.29",
|
|
36
|
+
"@tellescope/schema": "^0.0.29",
|
|
37
|
+
"@tellescope/testing": "^0.0.29",
|
|
38
|
+
"@tellescope/types-client": "^0.0.29",
|
|
39
|
+
"@tellescope/types-models": "^0.0.29",
|
|
40
|
+
"@tellescope/types-utilities": "^0.0.29",
|
|
41
|
+
"@tellescope/utilities": "^0.0.29",
|
|
42
|
+
"@tellescope/validation": "^0.0.29",
|
|
43
43
|
"axios": "^0.21.1",
|
|
44
44
|
"express": "^4.17.1",
|
|
45
45
|
"form-data": "^4.0.0",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "7e0f3d4785c144284c5dda119d6bccafb6fe6bc4"
|
|
60
60
|
}
|
package/src/enduser.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Session, SessionOptions } from "./session"
|
|
|
4
4
|
import { APIQuery } from "./sdk"
|
|
5
5
|
import { url_safe_path } from "@tellescope/utilities"
|
|
6
6
|
|
|
7
|
-
import { FileBlob, S3PresignedPost, UserIdentity } from "@tellescope/types-utilities"
|
|
7
|
+
import { FileBlob, FileBuffer, FileDetails, ReactNativeFile, S3PresignedPost, UserIdentity } from "@tellescope/types-utilities"
|
|
8
8
|
import {
|
|
9
9
|
Attendee,
|
|
10
10
|
} from "@tellescope/types-models"
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
|
|
20
20
|
export interface EnduserSessionOptions extends SessionOptions {}
|
|
21
21
|
|
|
22
|
-
type EnduserAccessibleModels = "chat_rooms" | 'chats' | 'files' | 'tickets'
|
|
22
|
+
type EnduserAccessibleModels = 'endusers' | "chat_rooms" | 'chats' | 'files' | 'tickets'
|
|
23
23
|
|
|
24
24
|
export const defaultQueries = <N extends keyof ClientModelForName>(
|
|
25
25
|
s: EnduserSession, n: keyof ClientModelForName_required
|
|
@@ -47,7 +47,7 @@ type EnduserQueries = { [K in EnduserAccessibleModels]: APIQuery<K> } & {
|
|
|
47
47
|
display_info: () => Promise<UserDisplayInfo[]>
|
|
48
48
|
},
|
|
49
49
|
files: {
|
|
50
|
-
prepare_file_upload: (args:
|
|
50
|
+
prepare_file_upload: (args: FileDetails) => Promise<{ presignedUpload: S3PresignedPost, file: File }>,
|
|
51
51
|
file_download_URL: (args: { secureName: string }) => Promise<{ downloadURL: string }>,
|
|
52
52
|
},
|
|
53
53
|
meetings: {
|
|
@@ -60,6 +60,7 @@ type EnduserQueries = { [K in EnduserAccessibleModels]: APIQuery<K> } & {
|
|
|
60
60
|
const loadDefaultQueries = (s: EnduserSession): { [K in EnduserAccessibleModels] : APIQuery<K> } => ({
|
|
61
61
|
chat_rooms: defaultQueries(s, 'chat_rooms'),
|
|
62
62
|
chats: defaultQueries(s, 'chats'),
|
|
63
|
+
endusers: defaultQueries(s, 'endusers'),
|
|
63
64
|
files: defaultQueries(s, 'files'),
|
|
64
65
|
tickets: defaultQueries(s, 'tickets'),
|
|
65
66
|
})
|
|
@@ -75,9 +76,8 @@ export class EnduserSession extends Session {
|
|
|
75
76
|
|
|
76
77
|
this.api = loadDefaultQueries(this) as EnduserQueries
|
|
77
78
|
|
|
78
|
-
this.api.endusers =
|
|
79
|
-
|
|
80
|
-
}
|
|
79
|
+
this.api.endusers.logout = () => this._POST('/v1/logout-enduser'),
|
|
80
|
+
|
|
81
81
|
this.api.users = {
|
|
82
82
|
display_info: () => this._GET<{}, UserDisplayInfo[] >(`/v1/user-display-info`),
|
|
83
83
|
}
|
|
@@ -113,8 +113,8 @@ export class EnduserSession extends Session {
|
|
|
113
113
|
return await this.DELETE<A,R>(endpoint, args, authenticated)
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
prepare_and_upload_file = async (file:
|
|
117
|
-
const { name, size, type } =
|
|
116
|
+
prepare_and_upload_file = async (details: FileDetails, file: Blob | Buffer | ReactNativeFile) => {
|
|
117
|
+
const { name, size, type } = details
|
|
118
118
|
const { presignedUpload, file: { secureName } } = await this.api.files.prepare_file_upload({ name, size, type })
|
|
119
119
|
await this.UPLOAD(presignedUpload, file)
|
|
120
120
|
return { secureName }
|
package/src/sdk.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
File,
|
|
18
18
|
Meeting,
|
|
19
19
|
} from "@tellescope/types-client"
|
|
20
|
-
import { CustomUpdateOptions, SortOption, S3PresignedPost, UserIdentity, FileBlob } from "@tellescope/types-utilities"
|
|
20
|
+
import { CustomUpdateOptions, SortOption, S3PresignedPost, UserIdentity, FileBlob, FileBuffer, FileDetails, ReactNativeFile } from "@tellescope/types-utilities"
|
|
21
21
|
import { url_safe_path } from "@tellescope/utilities"
|
|
22
22
|
|
|
23
23
|
import { Session as SessionManager, SessionOptions } from "./session"
|
|
@@ -92,14 +92,14 @@ type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
|
|
|
92
92
|
},
|
|
93
93
|
endusers: {
|
|
94
94
|
set_password: (args: { id: string, password: string }) => Promise<void>,
|
|
95
|
-
is_authenticated: (args: { id
|
|
95
|
+
is_authenticated: (args: { id?: string, authToken: string }) => Promise<{ isAuthenticated: boolean, enduser: Enduser }>
|
|
96
96
|
generate_auth_token: (args: { id?: string, phone?: string, email?: string, externalId?: string }) => Promise<{ authToken: string, enduser: Enduser }>
|
|
97
97
|
},
|
|
98
98
|
users: {
|
|
99
99
|
display_names: () => Promise<{ fname: string, lname: string, id: string }[]>,
|
|
100
100
|
},
|
|
101
101
|
files: {
|
|
102
|
-
prepare_file_upload: (args:
|
|
102
|
+
prepare_file_upload: (args: FileDetails) => Promise<{ presignedUpload: S3PresignedPost, file: File }>,
|
|
103
103
|
file_download_URL: (args: { secureName: string }) => Promise<{ downloadURL: string }>,
|
|
104
104
|
},
|
|
105
105
|
meetings: {
|
|
@@ -146,18 +146,7 @@ export class Session extends SessionManager {
|
|
|
146
146
|
queries.webhooks.configure = a => this._POST('/v1/configure-webhooks', a)
|
|
147
147
|
queries.webhooks.update = a => this._PATCH('/v1/update-webhooks', a)
|
|
148
148
|
|
|
149
|
-
const updateUser = queries.users.updateOne
|
|
150
|
-
queries.users.updateOne = async (...a: Parameters<typeof updateUser>) => {
|
|
151
|
-
const updated = await updateUser(...a)
|
|
152
|
-
if (a[0] === this.userInfo.id) { // ensure session userInfo is updated as well
|
|
153
|
-
await this.refresh_session()
|
|
154
|
-
}
|
|
155
|
-
return updated
|
|
156
|
-
}
|
|
157
|
-
|
|
158
149
|
this.api = queries
|
|
159
|
-
|
|
160
|
-
// if (this.userInfo) this.refresh_session()
|
|
161
150
|
}
|
|
162
151
|
|
|
163
152
|
_POST = async <A,R=void>(endpoint: string, args?: A, authenticated=true) => {
|
|
@@ -213,8 +202,8 @@ export class Session extends SessionManager {
|
|
|
213
202
|
await this.POST('/logout-api').catch(console.error)
|
|
214
203
|
}
|
|
215
204
|
|
|
216
|
-
prepare_and_upload_file = async (file:
|
|
217
|
-
const { name, size, type } =
|
|
205
|
+
prepare_and_upload_file = async (details: FileDetails, file: Blob | Buffer | ReactNativeFile) => {
|
|
206
|
+
const { name, size, type } = details
|
|
218
207
|
const { presignedUpload, file: { secureName } } = await this.api.files.prepare_file_upload({ name, size, type })
|
|
219
208
|
await this.UPLOAD(presignedUpload, file)
|
|
220
209
|
return { secureName }
|
package/src/session.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Socket, io } from 'socket.io-client'
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
FileBlob,
|
|
7
|
+
ReactNativeFile,
|
|
7
8
|
S3PresignedPost,
|
|
8
9
|
} from "@tellescope/types-utilities"
|
|
9
10
|
import {
|
|
@@ -152,27 +153,17 @@ export class Session {
|
|
|
152
153
|
} catch(err) { throw await this.errorHandler(err) }
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
UPLOAD = async (presigned: S3PresignedPost, file
|
|
156
|
-
const formData =
|
|
156
|
+
UPLOAD = async (presigned: S3PresignedPost, file: Blob | Buffer | ReactNativeFile) => {
|
|
157
|
+
const formData = new NodeFormData();
|
|
157
158
|
Object.keys(presigned.fields).forEach(key => {
|
|
158
|
-
formData.append(key, presigned.fields[key]);
|
|
159
|
+
formData.append(key, presigned.fields[key as keyof S3PresignedPost['fields']]);
|
|
159
160
|
});
|
|
161
|
+
formData.append("file", file) // must be appended last
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
formData.append("file", file as Blob)
|
|
163
|
+
const headers = formData.getHeaders ? { ...formData.getHeaders(), 'Content-Length' : (formData as any).maxDataSize }
|
|
164
|
+
: { 'Content-Type': 'multipart/form-data' }
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
await axios.post(presigned.url, formData, {
|
|
167
|
-
headers: IN_BROWSER
|
|
168
|
-
? { 'Content-Type': 'multipart/form-data'}
|
|
169
|
-
: {
|
|
170
|
-
...(formData as NodeFormData).getHeaders(),
|
|
171
|
-
'Content-Length' : (formData as any).maxDataSize
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
catch(err) { console.error(err); throw err }
|
|
166
|
+
await axios.post(presigned.url, formData, { headers })
|
|
176
167
|
}
|
|
177
168
|
|
|
178
169
|
DOWNLOAD = async (downloadURL: string) => {
|
|
@@ -181,6 +172,9 @@ export class Session {
|
|
|
181
172
|
}
|
|
182
173
|
|
|
183
174
|
EMIT = async (route: string, args: object, authenticated=true, options={} as RequestOptions) => {
|
|
175
|
+
if (!this.socket) {
|
|
176
|
+
this.authenticate_socket() // sets namespace correctly
|
|
177
|
+
}
|
|
184
178
|
this.socket?.emit(route, { ...args, ...authenticated ? { authToken: this.authToken } : {} } )
|
|
185
179
|
}
|
|
186
180
|
|
|
@@ -199,6 +193,7 @@ export class Session {
|
|
|
199
193
|
removeAllSocketListeners = (s: string) => this.socket?.removeAllListeners(s)
|
|
200
194
|
|
|
201
195
|
authenticate_socket = () => {
|
|
196
|
+
if (!this.userInfo?.businessId) return
|
|
202
197
|
this.socket = io(`${this.host}/${this.userInfo.businessId}`, { transports: ['websocket'] }); // supporting polling requires sticky session at load balancer
|
|
203
198
|
this.socket.on('disconnect', () => { this.socketAuthenticated = false })
|
|
204
199
|
this.socket.on('authenticated', () => { this.socketAuthenticated = true })
|
package/src/tests/tests.ts
CHANGED
|
@@ -295,8 +295,13 @@ const generateEnduserAuthTests = async () => {
|
|
|
295
295
|
|
|
296
296
|
const { authToken, enduser } = await sdk.api.endusers.generate_auth_token({ id: e.id })
|
|
297
297
|
assert(!!authToken && !!enduser, 'invalid returned values', 'Generate authToken and get enduser')
|
|
298
|
-
|
|
298
|
+
let { isAuthenticated } = await sdk.api.endusers.is_authenticated({ id: enduser.id, authToken })
|
|
299
299
|
assert(isAuthenticated, 'invalid authToken generated for enduser', 'Generate authToken for enduser is valid')
|
|
300
|
+
assert(
|
|
301
|
+
(await sdk.api.endusers.is_authenticated({ authToken })).isAuthenticated,
|
|
302
|
+
'id omitted results in failed authentication',
|
|
303
|
+
'id optional for is_authenticated'
|
|
304
|
+
)
|
|
300
305
|
|
|
301
306
|
const { authToken: authTokenUID, enduser: enduser2 } = await sdk.api.endusers.generate_auth_token({ externalId })
|
|
302
307
|
assert(!!authTokenUID && !!enduser2, 'invalid returned values eid', 'Generate authToken and get enduser eid')
|
|
@@ -969,6 +974,7 @@ const enduserAccessTests = async () => {
|
|
|
969
974
|
const password = 'testpassword'
|
|
970
975
|
|
|
971
976
|
const enduser = await sdk.api.endusers.createOne({ email })
|
|
977
|
+
const enduser2 = await sdk.api.endusers.createOne({ email: 'hi' + email })
|
|
972
978
|
await sdk.api.endusers.set_password({ id: enduser.id, password }).catch(console.error)
|
|
973
979
|
await enduserSDK.authenticate(email, password).catch(console.error)
|
|
974
980
|
|
|
@@ -1021,6 +1027,17 @@ const enduserAccessTests = async () => {
|
|
|
1021
1027
|
}
|
|
1022
1028
|
}
|
|
1023
1029
|
|
|
1030
|
+
await async_test(
|
|
1031
|
+
`enduser can update self`,
|
|
1032
|
+
() => enduserSDK.api.endusers.updateOne(enduser.id, { fname: "Sebastian", lname: "Coates" }),
|
|
1033
|
+
{ onResult: e => e.id === enduser.id && e.fname === 'Sebastian' && e.lname === "Coates" }
|
|
1034
|
+
)
|
|
1035
|
+
await async_test(
|
|
1036
|
+
`enduser can't update other enduser`,
|
|
1037
|
+
() => enduserSDK.api.endusers.updateOne(enduser2.id, { fname: "Shouldn't Work"}),
|
|
1038
|
+
{ shouldError: true, onError: e => e.message === "Could not find a record for the given id" }
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1024
1041
|
const ticketAccessible = await sdk.api.tickets.createOne({ enduserId: enduser.id, title: "Accessible ticket" })
|
|
1025
1042
|
const ticketInaccessible = await sdk.api.tickets.createOne({ enduserId: PLACEHOLDER_ID, title: "Inaccessible ticket" })
|
|
1026
1043
|
await async_test(
|
|
@@ -1047,6 +1064,7 @@ const enduserAccessTests = async () => {
|
|
|
1047
1064
|
await sdk.api.tickets.deleteOne(ticketAccessible.id)
|
|
1048
1065
|
await sdk.api.tickets.deleteOne(ticketInaccessible.id)
|
|
1049
1066
|
await sdk.api.endusers.deleteOne(enduser.id)
|
|
1067
|
+
await sdk.api.endusers.deleteOne(enduser2.id)
|
|
1050
1068
|
}
|
|
1051
1069
|
|
|
1052
1070
|
const files_tests = async () => {
|
|
@@ -1055,12 +1073,15 @@ const files_tests = async () => {
|
|
|
1055
1073
|
const { presignedUpload, file } = await sdk.api.files.prepare_file_upload({
|
|
1056
1074
|
name: 'Test File', size: buff.byteLength, type: 'text/plain'
|
|
1057
1075
|
})
|
|
1058
|
-
await sdk.UPLOAD(presignedUpload,
|
|
1076
|
+
await sdk.UPLOAD(presignedUpload, buff)
|
|
1059
1077
|
|
|
1060
1078
|
const { downloadURL } = await sdk.api.files.file_download_URL({ secureName: file.secureName })
|
|
1061
1079
|
const downloaded: string = await sdk.DOWNLOAD(downloadURL)
|
|
1062
1080
|
|
|
1063
1081
|
assert(downloaded === buff.toString(), 'downloaded file does not match uploaded file', 'upload, download comparison')
|
|
1082
|
+
|
|
1083
|
+
const { downloadURL: cachedURL } = await sdk.api.files.file_download_URL({ secureName: file.secureName })
|
|
1084
|
+
assert(downloadURL === cachedURL, 'cache download url failed', 'download url cache')
|
|
1064
1085
|
}
|
|
1065
1086
|
|
|
1066
1087
|
const enduser_session_tests = async () => {
|
|
@@ -1097,14 +1118,15 @@ const users_tests = async () => {
|
|
|
1097
1118
|
)
|
|
1098
1119
|
|
|
1099
1120
|
// reset fname to "Non" if this test throws, otherwise will falsely pass on next run
|
|
1100
|
-
|
|
1121
|
+
// NOT Supported behavior any more
|
|
1122
|
+
// assert(sdkNonAdmin.userInfo.fname === 'Updated', 'refresh_session not called on self after update', 'sdk updated on user update')
|
|
1101
1123
|
|
|
1102
1124
|
await async_test(
|
|
1103
1125
|
`update user (admin, other user)`,
|
|
1104
1126
|
() => sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { fname: 'Non' }), // change back
|
|
1105
1127
|
{ onResult: u => u.id === sdkNonAdmin.userInfo.id && u.fname === "Non" }
|
|
1106
1128
|
)
|
|
1107
|
-
sdkNonAdmin.userInfo.fname = 'Non' // update back in sdk instance as well
|
|
1129
|
+
// sdkNonAdmin.userInfo.fname = 'Non' // update back in sdk instance as well
|
|
1108
1130
|
|
|
1109
1131
|
await async_test(
|
|
1110
1132
|
`verify user update with admin get`,
|