@tellescope/sdk 0.0.15 → 0.0.19
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 +10 -5
- package/lib/cjs/enduser.d.ts.map +1 -1
- package/lib/cjs/enduser.js +5 -4
- package/lib/cjs/enduser.js.map +1 -1
- package/lib/cjs/sdk.d.ts +20 -10
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +9 -10
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/session.d.ts +4 -21
- package/lib/cjs/session.d.ts.map +1 -1
- package/lib/cjs/session.js +1 -0
- package/lib/cjs/session.js.map +1 -1
- package/lib/cjs/tests/public_endpoint_tests.js +4 -4
- package/lib/cjs/tests/public_endpoint_tests.js.map +1 -1
- package/lib/cjs/tests/socket_tests.d.ts +1 -1
- package/lib/cjs/tests/socket_tests.d.ts.map +1 -1
- package/lib/cjs/tests/socket_tests.js +125 -8
- package/lib/cjs/tests/socket_tests.js.map +1 -1
- package/lib/cjs/tests/tests.js +147 -101
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/enduser.d.ts +10 -5
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/enduser.js +5 -4
- package/lib/esm/enduser.js.map +1 -1
- package/lib/esm/sdk.d.ts +20 -10
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +9 -10
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/session.d.ts +4 -21
- package/lib/esm/session.d.ts.map +1 -1
- package/lib/esm/session.js +1 -0
- package/lib/esm/session.js.map +1 -1
- package/lib/esm/tests/public_endpoint_tests.js +4 -4
- package/lib/esm/tests/public_endpoint_tests.js.map +1 -1
- package/lib/esm/tests/socket_tests.d.ts +1 -1
- package/lib/esm/tests/socket_tests.d.ts.map +1 -1
- package/lib/esm/tests/socket_tests.js +123 -8
- package/lib/esm/tests/socket_tests.js.map +1 -1
- package/lib/esm/tests/tests.js +147 -101
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/enduser.ts +12 -8
- package/src/sdk.ts +28 -20
- package/src/session.ts +3 -18
- package/src/tests/public_endpoint_tests.ts +4 -4
- package/src/tests/socket_tests.ts +68 -5
- package/src/tests/tests.ts +67 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Code for interacting with the Tellescope API",
|
|
5
5
|
"main": "./lib/cjs/sdk.js",
|
|
6
6
|
"module": "./lib/esm/sdk.js",
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/tellescope-os/tellescope#readme",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@tellescope/constants": "^0.0.
|
|
34
|
-
"@tellescope/schema": "^0.0.
|
|
35
|
-
"@tellescope/testing": "^0.0.
|
|
36
|
-
"@tellescope/types-client": "^0.0.
|
|
37
|
-
"@tellescope/types-models": "^0.0.
|
|
38
|
-
"@tellescope/types-utilities": "^0.0.
|
|
39
|
-
"@tellescope/utilities": "^0.0.
|
|
40
|
-
"@tellescope/validation": "^0.0.
|
|
33
|
+
"@tellescope/constants": "^0.0.19",
|
|
34
|
+
"@tellescope/schema": "^0.0.19",
|
|
35
|
+
"@tellescope/testing": "^0.0.19",
|
|
36
|
+
"@tellescope/types-client": "^0.0.19",
|
|
37
|
+
"@tellescope/types-models": "^0.0.19",
|
|
38
|
+
"@tellescope/types-utilities": "^0.0.19",
|
|
39
|
+
"@tellescope/utilities": "^0.0.19",
|
|
40
|
+
"@tellescope/validation": "^0.0.19",
|
|
41
41
|
"axios": "^0.21.1",
|
|
42
42
|
"form-data": "^4.0.0",
|
|
43
43
|
"socket.io-client": "^4.2.0",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "ddee8ca945d3daf7355a9eada1305666e56d7434"
|
|
57
57
|
}
|
package/src/enduser.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { io } from 'socket.io-client'
|
|
2
2
|
|
|
3
|
-
import { Session, SessionOptions
|
|
3
|
+
import { Session, SessionOptions } from "./session"
|
|
4
|
+
import { APIQuery } from "./sdk"
|
|
4
5
|
import { url_safe_path } from "@tellescope/utilities"
|
|
5
6
|
|
|
6
7
|
import { S3PresignedPost, UserIdentity } from "@tellescope/types-utilities"
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
Enduser,
|
|
15
16
|
File,
|
|
16
17
|
} from "@tellescope/types-client"
|
|
17
|
-
import { stringValidator } from "@tellescope/validation";
|
|
18
18
|
|
|
19
19
|
export interface EnduserSessionOptions extends SessionOptions {}
|
|
20
20
|
|
|
@@ -30,8 +30,9 @@ export const defaultQueries = <N extends keyof ClientModelForName>(
|
|
|
30
30
|
return {
|
|
31
31
|
createOne: o => s._POST(`/v1/${singularName}`, o),
|
|
32
32
|
createSome: os => s._POST(`/v1/${safeName}`, { create: os }),
|
|
33
|
-
getOne: (
|
|
34
|
-
|
|
33
|
+
getOne: (argument) => typeof argument === 'string' ? s._GET(`/v1/${singularName}/${argument}`)
|
|
34
|
+
: s._GET(`/v1/${singularName}`, { filter: argument}),
|
|
35
|
+
getSome: o => s._GET(`/v1/${safeName}`, o),
|
|
35
36
|
updateOne: (id, updates, options) => s._PATCH(`/v1/${singularName}/${id}`, { updates, options }),
|
|
36
37
|
deleteOne: id => s._DELETE(`/v1/${singularName}/${id}`),
|
|
37
38
|
}
|
|
@@ -42,7 +43,7 @@ type EnduserQueries = { [K in EnduserAccessibleModels]: APIQuery<K> } & {
|
|
|
42
43
|
logout: () => Promise<void>;
|
|
43
44
|
},
|
|
44
45
|
users: {
|
|
45
|
-
|
|
46
|
+
display_info: () => Promise<{ fname?: string, lname?: string, id: string, lastActive?: Date, lastLogout?: Date }[]>
|
|
46
47
|
},
|
|
47
48
|
files: {
|
|
48
49
|
prepare_file_upload: (args: { name: string, size: number, type: string }) => Promise<{ presignedUpload: S3PresignedPost, file: File }>,
|
|
@@ -76,7 +77,7 @@ export class EnduserSession extends Session {
|
|
|
76
77
|
logout: () => this._POST('/v1/logout-enduser'),
|
|
77
78
|
}
|
|
78
79
|
this.api.users = {
|
|
79
|
-
|
|
80
|
+
display_info: () => this._GET<{}, { fname: string, lname: string, id: string }[] >(`/v1/user-display-info`),
|
|
80
81
|
}
|
|
81
82
|
this.api.meetings = {
|
|
82
83
|
attendee_info: a => this._GET('/v1/attendee-info', a),
|
|
@@ -124,8 +125,11 @@ export class EnduserSession extends Session {
|
|
|
124
125
|
return { authToken, enduser }
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
authenticate = async (email: string, password: string) => this.handle_new_session(
|
|
128
|
-
await this.POST<
|
|
128
|
+
authenticate = async (email: string, password: string, o?: { expirationInSeconds?: number }) => this.handle_new_session(
|
|
129
|
+
await this.POST<
|
|
130
|
+
{email: string, password: string, expirationInSeconds?: number },
|
|
131
|
+
{ authToken: string, enduser: Enduser }
|
|
132
|
+
>('/v1/login-enduser', { email, password, ...o })
|
|
129
133
|
)
|
|
130
134
|
|
|
131
135
|
refresh_session = async () => {
|
package/src/sdk.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AttendeeInfo,
|
|
3
3
|
JourneyState,
|
|
4
|
-
MeetingStatus,
|
|
5
4
|
Meeting,
|
|
6
5
|
UserSession,
|
|
7
6
|
MeetingInfo,
|
|
7
|
+
ReadFilter,
|
|
8
8
|
} from "@tellescope/types-models"
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -19,12 +19,18 @@ import {
|
|
|
19
19
|
import { CustomUpdateOptions, SortOption, S3PresignedPost, UserIdentity } from "@tellescope/types-utilities"
|
|
20
20
|
import { url_safe_path } from "@tellescope/utilities"
|
|
21
21
|
|
|
22
|
-
import { Session as SessionManager, SessionOptions
|
|
22
|
+
import { Session as SessionManager, SessionOptions } from "./session"
|
|
23
23
|
|
|
24
24
|
export * from "./public"
|
|
25
25
|
export * from "./enduser"
|
|
26
26
|
|
|
27
|
-
export type LoadFunction<T> = (o?: {
|
|
27
|
+
export type LoadFunction<T> = (o?: {
|
|
28
|
+
lastId?: string,
|
|
29
|
+
limit?: number,
|
|
30
|
+
sort?: SortOption,
|
|
31
|
+
threadKey?: string,
|
|
32
|
+
filter?: ReadFilter<T>,
|
|
33
|
+
}) => Promise<T[]>
|
|
28
34
|
|
|
29
35
|
export interface APIQuery<
|
|
30
36
|
N extends keyof ClientModelForName,
|
|
@@ -36,7 +42,7 @@ export interface APIQuery<
|
|
|
36
42
|
{
|
|
37
43
|
createOne: (t: CREATE) => Promise<T>;
|
|
38
44
|
createSome: (ts: CREATE[]) => Promise<{ created: T[], errors: object[] }>;
|
|
39
|
-
getOne: (
|
|
45
|
+
getOne: (argument: string | ReadFilter<T>) => Promise<T>;
|
|
40
46
|
getSome: LoadFunction<T>;
|
|
41
47
|
updateOne: (id: string, updates: UPDATE, options?: CustomUpdateOptions) => Promise<T>;
|
|
42
48
|
deleteOne: (id: string) => Promise<void>;
|
|
@@ -52,7 +58,8 @@ export const defaultQueries = <N extends keyof ClientModelForName>(
|
|
|
52
58
|
return {
|
|
53
59
|
createOne: o => s._POST(`/v1/${singularName}`, o),
|
|
54
60
|
createSome: os => s._POST(`/v1/${safeName}`, { create: os }),
|
|
55
|
-
getOne: (
|
|
61
|
+
getOne: (argument) => typeof argument === 'string' ? s._GET(`/v1/${singularName}/${argument}`)
|
|
62
|
+
: s._GET(`/v1/${singularName}`, { filter: argument }),
|
|
56
63
|
getSome: (o) => s._GET(`/v1/${safeName}`, o),
|
|
57
64
|
updateOne: (id, updates, options) => s._PATCH(`/v1/${singularName}/${id}`, { updates, options }),
|
|
58
65
|
deleteOne: id => s._DELETE(`/v1/${singularName}/${id}`),
|
|
@@ -79,12 +86,12 @@ const loadDefaultQueries = (s: Session): { [K in keyof ClientModelForName] : API
|
|
|
79
86
|
|
|
80
87
|
type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
|
|
81
88
|
journeys: {
|
|
82
|
-
|
|
89
|
+
update_state: (args: { id: string, name: string, updates: JourneyState }) => Promise<void>
|
|
83
90
|
},
|
|
84
91
|
endusers: {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
set_password: (args: { id: string, password: string }) => Promise<void>,
|
|
93
|
+
is_authenticated: (args: { id: string, authToken: string }) => Promise<{ isAuthenticated: boolean, enduser: Enduser }>
|
|
94
|
+
generate_auth_token: (args: { id?: string, phone?: string, email?: string, externalId?: string }) => Promise<{ authToken: string, enduser: Enduser }>
|
|
88
95
|
},
|
|
89
96
|
users: {
|
|
90
97
|
display_names: () => Promise<{ fname: string, lname: string, id: string }[]>,
|
|
@@ -113,10 +120,10 @@ export class Session extends SessionManager {
|
|
|
113
120
|
super({ ...o, cacheKey: o?.cacheKey || "tellescope_user" })
|
|
114
121
|
const queries = loadDefaultQueries(this) as Queries
|
|
115
122
|
|
|
116
|
-
queries.journeys.
|
|
117
|
-
queries.endusers.
|
|
118
|
-
queries.endusers.
|
|
119
|
-
queries.endusers.
|
|
123
|
+
queries.journeys.update_state = ({id, name, updates}) => this._PATCH(`/v1/journey/${id}/state/${name}`, { updates })
|
|
124
|
+
queries.endusers.set_password = ({id, password}) => this._POST(`/v1/set-enduser-password`, { id, password })
|
|
125
|
+
queries.endusers.is_authenticated = ({id, authToken}) => this._GET(`/v1/enduser-is-authenticated`, { id, authToken })
|
|
126
|
+
queries.endusers.generate_auth_token = args => this._GET(`/v1/generate-enduser-auth-token`, args)
|
|
120
127
|
queries.users.display_names = () => this._GET<{}, { fname: string, lname: string, id: string }[]>(`/v1/user-display-names`),
|
|
121
128
|
queries.files.prepare_file_upload = (args) => this._POST(`/v1/prepare-file-upload`, args),
|
|
122
129
|
queries.files.file_download_URL = a => this._GET('/v1/file-download-URL', a),
|
|
@@ -173,13 +180,14 @@ export class Session extends SessionManager {
|
|
|
173
180
|
if (this.AUTO_REFRESH_MS < elapsedSessionMS) { return await this.refresh_session()}
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
authenticate = async (email: string, password: string,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
authenticate = async (email: string, password: string, o?: { expirationInSeconds?: number }) => (
|
|
184
|
+
this.handle_new_session(
|
|
185
|
+
await this.POST<
|
|
186
|
+
{email: string, password: string, expirationInSeconds?: number },
|
|
187
|
+
UserSession & { authToken: string }
|
|
188
|
+
>('/submit-login', { email, password, ...o })
|
|
189
|
+
)
|
|
190
|
+
)
|
|
183
191
|
logout = async () => {
|
|
184
192
|
this.clearState()
|
|
185
193
|
await this.POST('/logout-api').catch(console.error)
|
package/src/session.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface SessionOptions {
|
|
|
22
22
|
authToken?: string;
|
|
23
23
|
host?: string;
|
|
24
24
|
cacheKey?: string;
|
|
25
|
+
expirationInSeconds?: number,
|
|
25
26
|
handleUnauthenticated?: () => Promise<void>;
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -29,24 +30,6 @@ interface RequestOptions {
|
|
|
29
30
|
refresh_session?: () => Promise<any>,
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
export type Filter<T> = { [K in keyof T]: T[K] }
|
|
33
|
-
|
|
34
|
-
export interface APIQuery<
|
|
35
|
-
N extends keyof ClientModelForName,
|
|
36
|
-
T=ClientModelForName[N],
|
|
37
|
-
Req=ClientModelForName_required[N],
|
|
38
|
-
CREATE=Omit<Req & Partial<T>, keyof ClientModelForName_readonly[N]>,
|
|
39
|
-
UPDATE=Omit<Partial<T>, keyof (ClientModelForName_readonly[N] & ClientModelForName_updatesDisabled[N])>,
|
|
40
|
-
>
|
|
41
|
-
{
|
|
42
|
-
createOne: (t: CREATE) => Promise<T>;
|
|
43
|
-
createSome: (ts: CREATE[]) => Promise<{ created: T[], errors: object[] }>;
|
|
44
|
-
getOne: (id: string, filter?: Filter<Partial<T>>) => Promise<T>;
|
|
45
|
-
getSome: (o?: { lastId?: string, limit?: number, sort?: SortOption, threadKey?: string }, f?: Filter<Partial<T>>) => Promise<T[]>
|
|
46
|
-
updateOne: (id: string, updates: UPDATE, options?: CustomUpdateOptions) => Promise<T>;
|
|
47
|
-
deleteOne: (id: string) => Promise<void>;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
33
|
const generateBearer = (authToken: string) => `Bearer ${authToken}`
|
|
51
34
|
|
|
52
35
|
const parseError = (err: any) => {
|
|
@@ -71,6 +54,7 @@ export class Session {
|
|
|
71
54
|
apiKey?: string;
|
|
72
55
|
socket?: Socket;
|
|
73
56
|
handleUnauthenticated?: SessionOptions['handleUnauthenticated']
|
|
57
|
+
expirationInSeconds?: number;
|
|
74
58
|
socketAuthenticated: boolean;
|
|
75
59
|
userInfo: { businessId?: string };
|
|
76
60
|
sessionStart = Date.now();
|
|
@@ -81,6 +65,7 @@ export class Session {
|
|
|
81
65
|
constructor(o={} as SessionOptions & RequestOptions) {
|
|
82
66
|
this.host= o.host ?? DEFAULT_HOST
|
|
83
67
|
this.apiKey = o.apiKey ?? '';
|
|
68
|
+
this.expirationInSeconds = o.expirationInSeconds
|
|
84
69
|
this.socket = undefined as Socket | undefined
|
|
85
70
|
this.socketAuthenticated = false
|
|
86
71
|
this.handleUnauthenticated = o.handleUnauthenticated
|
|
@@ -32,14 +32,14 @@ const enduser_login_tests = async () => {
|
|
|
32
32
|
)
|
|
33
33
|
await async_test(
|
|
34
34
|
'setPassword',
|
|
35
|
-
() => sdk.api.endusers.
|
|
35
|
+
() => sdk.api.endusers.set_password({ id: e.id, password }),
|
|
36
36
|
{ onResult: _ => true }
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
let authToken = 'placeholder'
|
|
40
40
|
await async_test(
|
|
41
41
|
'isAuthenticated (no)',
|
|
42
|
-
() => sdk.api.endusers.
|
|
42
|
+
() => sdk.api.endusers.is_authenticated({ id: e.id, authToken }),
|
|
43
43
|
{ onResult: ({ isAuthenticated, enduser }) => isAuthenticated === false && enduser === null }
|
|
44
44
|
)
|
|
45
45
|
await async_test(
|
|
@@ -49,7 +49,7 @@ const enduser_login_tests = async () => {
|
|
|
49
49
|
)
|
|
50
50
|
await async_test(
|
|
51
51
|
'isAuthenticated (yes)',
|
|
52
|
-
() => sdk.api.endusers.
|
|
52
|
+
() => sdk.api.endusers.is_authenticated({ id: e.id, authToken }),
|
|
53
53
|
{ onResult: ({ isAuthenticated, enduser }) => isAuthenticated === true && enduser?.id === e.id }
|
|
54
54
|
)
|
|
55
55
|
}
|
|
@@ -58,7 +58,7 @@ const enduser_login_tests = async () => {
|
|
|
58
58
|
log_header("Public Endpoints")
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
|
-
await sdk.authenticate(email, password
|
|
61
|
+
await sdk.authenticate(email, password)
|
|
62
62
|
await sdk.reset_db()
|
|
63
63
|
|
|
64
64
|
await enduser_login_tests()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "crypto"
|
|
1
2
|
import {
|
|
2
3
|
assert,
|
|
3
4
|
async_test,
|
|
@@ -17,6 +18,7 @@ import { EnduserSession } from "../enduser"
|
|
|
17
18
|
import { Session, /* APIQuery */ } from "../sdk"
|
|
18
19
|
import { PLACEHOLDER_ID } from "@tellescope/constants"
|
|
19
20
|
|
|
21
|
+
export const get_sha256 = (s='') => createHash('sha256').update(s).digest('hex')
|
|
20
22
|
|
|
21
23
|
const host = process.env.TEST_URL || 'http://localhost:8080'
|
|
22
24
|
const [email, password] = [process.env.TEST_EMAIL, process.env.TEST_PASSWORD]
|
|
@@ -133,7 +135,7 @@ const access_tests = async () => {
|
|
|
133
135
|
|
|
134
136
|
const enduser_tests = async () => {
|
|
135
137
|
const enduser = await user1.api.endusers.createOne({ email: "enduser@tellescope.com" })
|
|
136
|
-
await user1.api.endusers.
|
|
138
|
+
await user1.api.endusers.set_password({ id: enduser.id, password: 'enduserPassword!' })
|
|
137
139
|
|
|
138
140
|
await enduserSDK.authenticate(enduser.email as string, 'enduserPassword!')
|
|
139
141
|
await wait(undefined, 25)
|
|
@@ -198,28 +200,89 @@ const enduser_tests = async () => {
|
|
|
198
200
|
// await user1.api.chats.deleteOne(messageToUser.id)
|
|
199
201
|
}
|
|
200
202
|
|
|
203
|
+
const TEST_SESSION_DURATION = 2 // seconds
|
|
204
|
+
const SESSION_TIMEOUT_DELAY = 2000 // ms
|
|
205
|
+
const deauthentication_tests = async (byTimeout=false) => {
|
|
206
|
+
log_header(`Unauthenticated Tests ${byTimeout ? '- With Timeout, requires Worker' : '- With Manual Logout' }`)
|
|
207
|
+
|
|
208
|
+
const enduser = await user1.api.endusers.createOne({ email: "socketenduser@tellescope.com" })
|
|
209
|
+
await user1.api.endusers.set_password({ id: enduser.id, password: 'enduserPassword!' })
|
|
210
|
+
await enduserSDK.authenticate(enduser.email as string, 'enduserPassword!', { expirationInSeconds: byTimeout ? TEST_SESSION_DURATION : undefined })
|
|
211
|
+
await wait(undefined, 25)
|
|
212
|
+
|
|
213
|
+
const room = await user1.api.chat_rooms.createOne({
|
|
214
|
+
type: 'external',
|
|
215
|
+
userIds: [user1.userInfo.id],
|
|
216
|
+
enduserIds: [enduser.id],
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const userEvents = [] as ChatMessage[]
|
|
220
|
+
const enduserEvents = [] as ChatMessage[]
|
|
221
|
+
user1.subscribe({ [room.id]: 'chats' })
|
|
222
|
+
enduserSDK.subscribe({ [room.id]: 'chats' })
|
|
223
|
+
user1.handle_events({ 'created-chats': rs => userEvents.push(...rs) })
|
|
224
|
+
enduserSDK.handle_events({ 'created-chats': rs => enduserEvents.push(...rs) })
|
|
225
|
+
await wait(undefined, 10)
|
|
226
|
+
|
|
227
|
+
if (!byTimeout) {
|
|
228
|
+
await enduserSDK.api.endusers.logout()
|
|
229
|
+
} else {
|
|
230
|
+
await wait(undefined, TEST_SESSION_DURATION * 1000 + SESSION_TIMEOUT_DELAY)
|
|
231
|
+
}
|
|
232
|
+
await user1.api.chats.createOne({ roomId: room.id, message: "Hello!" })
|
|
233
|
+
await wait(undefined, 25)
|
|
234
|
+
assert(objects_equivalent(enduserEvents[0], undefined), 'enduser got message after logout on socket', 'enduser logged out')
|
|
235
|
+
|
|
236
|
+
// re-authenticate enduser to send message to user
|
|
237
|
+
await enduserSDK.authenticate(enduser.email as string, 'enduserPassword!')
|
|
238
|
+
|
|
239
|
+
// ensure user logged out appropriately for not receiving message
|
|
240
|
+
await user1.logout()
|
|
241
|
+
if (byTimeout) {
|
|
242
|
+
await user1.authenticate(email, password, { expirationInSeconds: byTimeout ? TEST_SESSION_DURATION : undefined } )
|
|
243
|
+
await wait(undefined, TEST_SESSION_DURATION * 1000 + SESSION_TIMEOUT_DELAY)
|
|
244
|
+
}
|
|
245
|
+
await enduserSDK.api.chats.createOne({ roomId: room.id, message: "Hello right back!" })
|
|
246
|
+
await wait(undefined, 25)
|
|
247
|
+
assert(objects_equivalent(userEvents[0], undefined), 'user got message after logout', 'user logged out')
|
|
248
|
+
|
|
249
|
+
// must come before cleanup, so cleanup works
|
|
250
|
+
await user1.authenticate(email, password), // reauthenticate for later tests as needed
|
|
251
|
+
|
|
252
|
+
// cleanup
|
|
253
|
+
await Promise.all([
|
|
254
|
+
user1.api.endusers.deleteOne(enduser.id),
|
|
255
|
+
user1.api.chat_rooms.deleteOne(room.id), // deletes chats as side effect
|
|
256
|
+
])
|
|
257
|
+
}
|
|
258
|
+
|
|
201
259
|
(async () => {
|
|
202
260
|
log_header("Sockets")
|
|
203
261
|
|
|
204
262
|
try {
|
|
205
|
-
await user1.authenticate(email, password
|
|
263
|
+
await user1.authenticate(email, password)
|
|
206
264
|
await user1.reset_db()
|
|
207
|
-
await user2.authenticate(email2, password2
|
|
265
|
+
await user2.authenticate(email2, password2) // generate authToken + socket connection for API keyj
|
|
208
266
|
await wait(undefined, 25)
|
|
209
267
|
|
|
210
268
|
let loopCount = 0
|
|
211
269
|
while (!(user1.socketAuthenticated && user2.socketAuthenticated) && ++loopCount < 10) {
|
|
212
|
-
|
|
270
|
+
if (!user1.socketAuthenticated) user1.authenticate_socket()
|
|
271
|
+
if (!user2.socketAuthenticated) user2.authenticate_socket()
|
|
213
272
|
await wait(undefined, 100)
|
|
214
273
|
}
|
|
215
274
|
if (loopCount === 10) {
|
|
216
275
|
console.log("Failed to authenticate")
|
|
217
276
|
process.exit()
|
|
218
277
|
}
|
|
219
|
-
|
|
278
|
+
|
|
220
279
|
await enduser_tests()
|
|
221
280
|
await basic_tests()
|
|
222
281
|
await access_tests()
|
|
282
|
+
|
|
283
|
+
await deauthentication_tests() // should come last!
|
|
284
|
+
await deauthentication_tests(true) // should come last!
|
|
285
|
+
|
|
223
286
|
} catch(err) {
|
|
224
287
|
console.error(err)
|
|
225
288
|
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -71,7 +71,7 @@ const userId = '60398b0231a295e64f084fd9'
|
|
|
71
71
|
// await sdk.api.endusers.deleteOne(enduser.id)
|
|
72
72
|
// }
|
|
73
73
|
|
|
74
|
-
const sdk = new Session()
|
|
74
|
+
const sdk = new Session({ host })
|
|
75
75
|
const sdkOther = new Session({ host, apiKey: "ba745e25162bb95a795c5fa1af70df188d93c4d3aac9c48b34a5c8c9dd7b80f7" })
|
|
76
76
|
const enduserSDK = new EnduserSession({ host })
|
|
77
77
|
// const sdkOtherEmail = "sebass@tellescope.com"
|
|
@@ -90,8 +90,6 @@ const setup_tests = async () => {
|
|
|
90
90
|
await async_test('test_online', sdk.test_online, { expectedResult: 'API V1 Online' })
|
|
91
91
|
await async_test('test_authenticated', sdk.test_authenticated, { expectedResult: 'Authenticated!' })
|
|
92
92
|
|
|
93
|
-
// console.log(await sdk.api.api_keys.createOne({}))
|
|
94
|
-
|
|
95
93
|
await async_test(
|
|
96
94
|
'test_authenticated (with API Key)',
|
|
97
95
|
(new Session({ host, apiKey: '3n5q0SCBT_iUvZz-b9BJtX7o7HQUVJ9v132PgHJNJsg.' /* local test key */ })).test_authenticated,
|
|
@@ -100,7 +98,7 @@ const setup_tests = async () => {
|
|
|
100
98
|
|
|
101
99
|
await sdk.logout()
|
|
102
100
|
await async_test<string, string>('test_authenticated - (logout invalidates jwt)', sdk.test_authenticated, { shouldError: true, onError: e => e === 'Unauthenticated' })
|
|
103
|
-
await sdk.authenticate(email, password
|
|
101
|
+
await sdk.authenticate(email, password)
|
|
104
102
|
await async_test('test_authenticated (re-authenticated)', sdk.test_authenticated, { expectedResult: 'Authenticated!' })
|
|
105
103
|
|
|
106
104
|
await async_test('reset_db', () => sdk.reset_db(), passOnVoid)
|
|
@@ -191,6 +189,20 @@ const threadKeyTests = async () => {
|
|
|
191
189
|
])
|
|
192
190
|
}
|
|
193
191
|
|
|
192
|
+
const badInputTests = async () => {
|
|
193
|
+
log_header("Bad Input Tests")
|
|
194
|
+
await async_test(
|
|
195
|
+
`_-prefixed fields are not allowed`,
|
|
196
|
+
() => sdk.api.endusers.createOne({ email: 'failure@tellescope.com', fields: { "_notallowed": 'hello' } }),
|
|
197
|
+
{ shouldError: true, onError: e => e.message === "Error parsing field fields: Fields that start with '_' are not allowed" },
|
|
198
|
+
)
|
|
199
|
+
await async_test(
|
|
200
|
+
`_-prefixed fields are not allowed`,
|
|
201
|
+
() => sdk.api.notes.createOne({ enduserId: PLACEHOLDER_ID, fields: { "_notallowed": 'hello' } }),
|
|
202
|
+
{ shouldError: true, onError: e => e.message === "Error parsing field fields: Fields that start with '_' are not allowed" },
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
194
206
|
const filterTests = async () => {
|
|
195
207
|
const enduser = await sdk.api.endusers.createOne({ email: 'filtertests@tellescope.com', fname: 'test', fields: { field1: 'value1', field2: 'value2' } })
|
|
196
208
|
const otherEnduser = await sdk.api.endusers.createOne({ email: 'other@tellescope.com' })
|
|
@@ -199,6 +211,17 @@ const filterTests = async () => {
|
|
|
199
211
|
() => sdk.api.endusers.getSome({ filter: { email: 'filtertests@tellescope.com' }}),
|
|
200
212
|
{ onResult: es => es.length === 1 && es[0].id === enduser.id },
|
|
201
213
|
)
|
|
214
|
+
await async_test(
|
|
215
|
+
`find enduser by _exists filter`,
|
|
216
|
+
() => sdk.api.endusers.getSome({ filter: { fname: { _exists: true } }}),
|
|
217
|
+
{ onResult: es => es.length === 1 && es[0].id === enduser.id },
|
|
218
|
+
)
|
|
219
|
+
await async_test(
|
|
220
|
+
`find enduser by _exists filter (findOne)`,
|
|
221
|
+
// () => sdk.api.endusers.getOne({ fname: { _exists: true } }),
|
|
222
|
+
() => sdk.api.endusers.getOne({ fname: { _exists: true } }),
|
|
223
|
+
{ onResult: e => e.id === enduser.id },
|
|
224
|
+
)
|
|
202
225
|
await async_test(
|
|
203
226
|
`find enduser by compound filter`,
|
|
204
227
|
() => sdk.api.endusers.getSome({ filter: { email: 'filtertests@tellescope.com', fname: 'test' }}),
|
|
@@ -265,11 +288,31 @@ const updatesTests = async () => {
|
|
|
265
288
|
|
|
266
289
|
const generateEnduserAuthTests = async () => {
|
|
267
290
|
log_header("Generated Enduser authToken Tests")
|
|
268
|
-
const
|
|
269
|
-
const { authToken } = await sdk.api.endusers.
|
|
270
|
-
|
|
291
|
+
const e = await sdk.api.endusers.createOne({ email: 'generated@tellescope.com', phone: '+15555555555', externalId: '1029f9v9sjd0as' })
|
|
292
|
+
const { authToken, enduser } = await sdk.api.endusers.generate_auth_token({ id: e.id })
|
|
293
|
+
assert(!!authToken && !!enduser, 'invalid returned values', 'Generate authToken and get enduser')
|
|
294
|
+
|
|
295
|
+
const { isAuthenticated } = await sdk.api.endusers.is_authenticated({ id: enduser.id, authToken })
|
|
271
296
|
assert(isAuthenticated, 'invalid authToken generated for enduser', 'Generate authToken for enduser is valid')
|
|
272
297
|
|
|
298
|
+
await async_test(
|
|
299
|
+
`auth by externalId`, () => sdk.api.endusers.generate_auth_token({ externalId: e.externalId }), passOnVoid,
|
|
300
|
+
)
|
|
301
|
+
await async_test(
|
|
302
|
+
`auth by email`, () => sdk.api.endusers.generate_auth_token({ email: e.email }), passOnVoid,
|
|
303
|
+
)
|
|
304
|
+
await async_test(
|
|
305
|
+
`auth by phone`, () => sdk.api.endusers.generate_auth_token({ phone: e.phone }), passOnVoid,
|
|
306
|
+
)
|
|
307
|
+
await async_test(
|
|
308
|
+
`auth by nothing throws error`, () => sdk.api.endusers.generate_auth_token({ phone: undefined }),
|
|
309
|
+
{ shouldError: true, onError: e => e.message === "One of id, externalId, email, or phone is required" },
|
|
310
|
+
)
|
|
311
|
+
await async_test(
|
|
312
|
+
`auth by bad field throws error`, () => sdk.api.endusers.generate_auth_token({ email: "notavalidemail@tellescope.com" }),
|
|
313
|
+
{ shouldError: true, onError: e => e.message === "Could not find a corresponding enduser" },
|
|
314
|
+
)
|
|
315
|
+
|
|
273
316
|
await sdk.api.endusers.deleteOne(enduser.id)
|
|
274
317
|
}
|
|
275
318
|
|
|
@@ -399,7 +442,7 @@ const run_generated_tests = async <N extends ModelName>({ queries, model, name,
|
|
|
399
442
|
}
|
|
400
443
|
await async_test(
|
|
401
444
|
`get-${singularName}`,
|
|
402
|
-
() => queries.getOne(_id
|
|
445
|
+
() => queries.getOne(_id),
|
|
403
446
|
{ onResult: d => {
|
|
404
447
|
if (!d?.id) return false
|
|
405
448
|
|
|
@@ -432,7 +475,7 @@ const run_generated_tests = async <N extends ModelName>({ queries, model, name,
|
|
|
432
475
|
)
|
|
433
476
|
await async_test(
|
|
434
477
|
`get-${singularName} (verify delete)`,
|
|
435
|
-
() => queries.getOne(_id
|
|
478
|
+
() => queries.getOne(_id),
|
|
436
479
|
{ shouldError: true, onError: e => e.message === 'Could not find a record for the given id' }
|
|
437
480
|
)
|
|
438
481
|
}
|
|
@@ -640,7 +683,7 @@ const journey_tests = async (queries=sdk.api.journeys) => {
|
|
|
640
683
|
|
|
641
684
|
await async_test(
|
|
642
685
|
`journey-updateState`,
|
|
643
|
-
() => queries.
|
|
686
|
+
() => queries.update_state({ id: journey.id, name: 'Added', updates: { name: 'Updated', priority: 'N/A' }}),
|
|
644
687
|
passOnVoid,
|
|
645
688
|
)
|
|
646
689
|
await wait(undefined, 25) // wait for side effects to update endusers
|
|
@@ -820,32 +863,22 @@ const chat_tests = async() => {
|
|
|
820
863
|
const chat = await sdk.api.chats.createOne({ roomId: room.id, message: "Hello!" })
|
|
821
864
|
const chat2 = await sdk.api.chats.createOne({ roomId: room.id, message: "Hello..." })
|
|
822
865
|
|
|
823
|
-
await async_test(
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
)
|
|
866
|
+
// await async_test(
|
|
867
|
+
// `get-chat (without filter)`,
|
|
868
|
+
// () => sdk.api.chats.getOne(chat.id),
|
|
869
|
+
// { shouldError: true, onError: () => true }
|
|
870
|
+
// )
|
|
828
871
|
await async_test(
|
|
829
872
|
`get-chats (without filter)`,
|
|
830
873
|
() => sdk.api.chats.getSome({}),
|
|
831
874
|
{ shouldError: true, onError: () => true }
|
|
832
875
|
)
|
|
833
|
-
await async_test(
|
|
834
|
-
`get-chat (with filter)`,
|
|
835
|
-
() => sdk.api.chats.getOne(chat.id, { roomId: room.id }),
|
|
836
|
-
{ onResult: c => c?.id === chat.id }
|
|
837
|
-
)
|
|
838
876
|
await async_test(
|
|
839
877
|
`get-chats (with filter)`,
|
|
840
878
|
() => sdk.api.chats.getSome({ filter: { roomId: room.id } }),
|
|
841
879
|
{ onResult: c => c?.length === 2 }
|
|
842
880
|
)
|
|
843
881
|
|
|
844
|
-
await async_test(
|
|
845
|
-
`get-chat not allowed`,
|
|
846
|
-
() => sdk2.api.chats.getOne(chat.id, { roomId: room.id }),
|
|
847
|
-
{ shouldError: true, onError: e => e.message === 'You do not have permission to access this resource' }
|
|
848
|
-
)
|
|
849
882
|
await async_test(
|
|
850
883
|
`get-chats not allowed`,
|
|
851
884
|
() => sdk2.api.chats.getSome({ filter: { roomId: room.id } }),
|
|
@@ -874,12 +907,12 @@ const chat_tests = async() => {
|
|
|
874
907
|
await wait(undefined, 25)
|
|
875
908
|
await async_test(
|
|
876
909
|
`get-chat (deleted as dependency of room 1)`,
|
|
877
|
-
() => sdk.api.chats.getOne(chat.id
|
|
910
|
+
() => sdk.api.chats.getOne(chat.id),
|
|
878
911
|
{ shouldError: true, onError: e => e.message === 'Could not find a record for the given id' }
|
|
879
912
|
)
|
|
880
913
|
await async_test(
|
|
881
914
|
`get-chat (deleted as dependency of room 2)`,
|
|
882
|
-
() => sdk.api.chats.getOne(chat2.id
|
|
915
|
+
() => sdk.api.chats.getOne(chat2.id),
|
|
883
916
|
{ shouldError: true, onError: e => e.message === 'Could not find a record for the given id' }
|
|
884
917
|
)
|
|
885
918
|
|
|
@@ -888,12 +921,12 @@ const chat_tests = async() => {
|
|
|
888
921
|
const sharedChat2 = await sdk2.api.chats.createOne({ roomId: sharedRoom.id, message: "Hello there!", })
|
|
889
922
|
await async_test(
|
|
890
923
|
`get-chat (shared, user1)`,
|
|
891
|
-
() => sdk.api.chats.getOne(sharedChat.id
|
|
924
|
+
() => sdk.api.chats.getOne(sharedChat.id),
|
|
892
925
|
{ onResult: r => r.id === sharedChat.id }
|
|
893
926
|
)
|
|
894
927
|
await async_test(
|
|
895
928
|
`get-chat (shared, user2)`,
|
|
896
|
-
() => sdk2.api.chats.getOne(sharedChat.id
|
|
929
|
+
() => sdk2.api.chats.getOne(sharedChat.id),
|
|
897
930
|
{ onResult: r => r.id === sharedChat.id }
|
|
898
931
|
)
|
|
899
932
|
await async_test(
|
|
@@ -916,7 +949,7 @@ const chat_tests = async() => {
|
|
|
916
949
|
await sdk.api.chats.deleteOne(chatNull.id)
|
|
917
950
|
await async_test(
|
|
918
951
|
`get-chat (setNull working)`,
|
|
919
|
-
() => sdk.api.chats.getOne(chat2Null.id
|
|
952
|
+
() => sdk.api.chats.getOne(chat2Null.id),
|
|
920
953
|
{ onResult: c => c.replyId === null }
|
|
921
954
|
)
|
|
922
955
|
}
|
|
@@ -926,7 +959,7 @@ const enduserAccessTests = async () => {
|
|
|
926
959
|
const password = 'testpassword'
|
|
927
960
|
|
|
928
961
|
const enduser = await sdk.api.endusers.createOne({ email })
|
|
929
|
-
await sdk.api.endusers.
|
|
962
|
+
await sdk.api.endusers.set_password({ id: enduser.id, password }).catch(console.error)
|
|
930
963
|
await enduserSDK.authenticate(email, password).catch(console.error)
|
|
931
964
|
|
|
932
965
|
for (const n in schema) {
|
|
@@ -1038,13 +1071,13 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
|
|
|
1038
1071
|
};
|
|
1039
1072
|
|
|
1040
1073
|
(async () => {
|
|
1041
|
-
await sdk.authenticate(email, password, host)
|
|
1042
|
-
|
|
1043
1074
|
log_header("API")
|
|
1044
|
-
|
|
1075
|
+
|
|
1045
1076
|
try {
|
|
1077
|
+
await sdk.authenticate(email, password)
|
|
1046
1078
|
await setup_tests()
|
|
1047
1079
|
await multi_tenant_tests() // should come right after setup tests
|
|
1080
|
+
await badInputTests()
|
|
1048
1081
|
await filterTests()
|
|
1049
1082
|
await updatesTests()
|
|
1050
1083
|
await threadKeyTests()
|