@tellescope/sdk 0.0.4 → 0.0.8

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.
Files changed (45) hide show
  1. package/lib/cjs/enduser.d.ts +346 -13
  2. package/lib/cjs/enduser.d.ts.map +1 -1
  3. package/lib/cjs/enduser.js +135 -14
  4. package/lib/cjs/enduser.js.map +1 -1
  5. package/lib/cjs/sdk.d.ts +106 -19
  6. package/lib/cjs/sdk.d.ts.map +1 -1
  7. package/lib/cjs/sdk.js +116 -32
  8. package/lib/cjs/sdk.js.map +1 -1
  9. package/lib/cjs/session.d.ts +36 -175
  10. package/lib/cjs/session.d.ts.map +1 -1
  11. package/lib/cjs/session.js +131 -34
  12. package/lib/cjs/session.js.map +1 -1
  13. package/lib/cjs/tests/public_endpoint_tests.js +3 -3
  14. package/lib/cjs/tests/public_endpoint_tests.js.map +1 -1
  15. package/lib/cjs/tests/socket_tests.js +42 -31
  16. package/lib/cjs/tests/socket_tests.js.map +1 -1
  17. package/lib/cjs/tests/tests.js +196 -62
  18. package/lib/cjs/tests/tests.js.map +1 -1
  19. package/lib/esm/enduser.d.ts +346 -13
  20. package/lib/esm/enduser.d.ts.map +1 -1
  21. package/lib/esm/enduser.js +132 -12
  22. package/lib/esm/enduser.js.map +1 -1
  23. package/lib/esm/sdk.d.ts +106 -19
  24. package/lib/esm/sdk.d.ts.map +1 -1
  25. package/lib/esm/sdk.js +116 -32
  26. package/lib/esm/sdk.js.map +1 -1
  27. package/lib/esm/session.d.ts +36 -175
  28. package/lib/esm/session.d.ts.map +1 -1
  29. package/lib/esm/session.js +130 -32
  30. package/lib/esm/session.js.map +1 -1
  31. package/lib/esm/tests/public_endpoint_tests.js +3 -3
  32. package/lib/esm/tests/public_endpoint_tests.js.map +1 -1
  33. package/lib/esm/tests/socket_tests.js +41 -30
  34. package/lib/esm/tests/socket_tests.js.map +1 -1
  35. package/lib/esm/tests/tests.js +192 -63
  36. package/lib/esm/tests/tests.js.map +1 -1
  37. package/lib/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +11 -9
  39. package/src/enduser.ts +93 -20
  40. package/src/sdk.ts +70 -42
  41. package/src/session.ts +121 -33
  42. package/src/tests/public_endpoint_tests.ts +6 -4
  43. package/src/tests/socket_tests.ts +37 -16
  44. package/src/tests/tests.ts +117 -24
  45. package/tsconfig.json +0 -1
package/src/sdk.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { io } from 'socket.io-client'
2
-
3
1
  import {
4
2
  JourneyState,
5
3
  UserSession,
@@ -11,7 +9,9 @@ import {
11
9
  ClientModelForName_required,
12
10
  ClientModelForName_updatesDisabled,
13
11
  Enduser,
12
+ File,
14
13
  } from "@tellescope/types-client"
14
+ import { CustomUpdateOptions, SortOption, S3PresignedPost } from "@tellescope/types-utilities"
15
15
  import { url_safe_path } from "@tellescope/utilities"
16
16
 
17
17
  import { Session as SessionManager, SessionOptions, Filter } from "./session"
@@ -30,29 +30,29 @@ export interface APIQuery<
30
30
  createOne: (t: CREATE) => Promise<T>;
31
31
  createSome: (ts: CREATE[]) => Promise<{ created: T[], errors: object[] }>;
32
32
  getOne: (id: string, filter?: Filter<Partial<T>>) => Promise<T>;
33
- getSome: (o?: { lastId?: string, limit?: number, sort?: SortOption, threadKey?: string }, f?: Filter<Partial<T>>) => Promise<T[]>
33
+ getSome: (o?: { lastId?: string, limit?: number, sort?: SortOption, threadKey?: string, filter?: Filter<Partial<T>> }) => Promise<T[]>
34
34
  updateOne: (id: string, updates: UPDATE, options?: CustomUpdateOptions) => Promise<void>;
35
35
  deleteOne: (id: string) => Promise<void>;
36
36
  }
37
37
 
38
38
  export const defaultQueries = <N extends keyof ClientModelForName>(
39
- s: SessionManager, n: keyof ClientModelForName_required
39
+ s: Session, n: keyof ClientModelForName_required
40
40
  ): APIQuery<N> => {
41
41
 
42
42
  const safeName = url_safe_path(n)
43
43
  const singularName = (safeName).substring(0, safeName.length - 1)
44
44
 
45
45
  return {
46
- createOne: o => s.POST(`/v1/${singularName}`, o),
47
- createSome: os => s.POST(`/v1/${safeName}`, { create: os }),
48
- getOne: (id, filter) => s.GET(`/v1/${singularName}/${id}`, { filter }),
49
- getSome: (o, filter) => s.GET(`/v1/${safeName}`, { ...o, filter }),
50
- updateOne: (id, updates, options) => s.PATCH(`/v1/${singularName}/${id}`, { updates, options }),
51
- deleteOne: id => s.DELETE(`/v1/${singularName}/${id}`),
46
+ createOne: o => s._POST(`/v1/${singularName}`, o),
47
+ createSome: os => s._POST(`/v1/${safeName}`, { create: os }),
48
+ getOne: (id, filter) => s._GET(`/v1/${singularName}/${id}`, { filter }),
49
+ getSome: (o) => s._GET(`/v1/${safeName}`, o),
50
+ updateOne: (id, updates, options) => s._PATCH(`/v1/${singularName}/${id}`, { updates, options }),
51
+ deleteOne: id => s._DELETE(`/v1/${singularName}/${id}`),
52
52
  }
53
53
  }
54
54
 
55
- const loadDefaultQueries = (s: SessionManager): { [K in keyof ClientModelForName] : APIQuery<K> } => ({
55
+ const loadDefaultQueries = (s: Session): { [K in keyof ClientModelForName] : APIQuery<K> } => ({
56
56
  endusers: defaultQueries(s, 'endusers'),
57
57
  engagement_events: defaultQueries(s, 'engagement_events'),
58
58
  journeys: defaultQueries(s, 'journeys'),
@@ -63,52 +63,86 @@ const loadDefaultQueries = (s: SessionManager): { [K in keyof ClientModelForName
63
63
  chat_rooms: defaultQueries(s, 'chat_rooms'),
64
64
  chats: defaultQueries(s, 'chats'),
65
65
  users: defaultQueries(s, 'users'),
66
- templates: defaultQueries(s, 'templates') ,
66
+ templates: defaultQueries(s, 'templates'),
67
+ files: defaultQueries(s, 'files'),
67
68
  })
68
69
 
69
70
  type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
70
71
  journeys: {
71
- updateState: (id: string, name: string, updates: JourneyState) => Promise<void>
72
+ updateState: (args: { id: string, name: string, updates: JourneyState }) => Promise<void>
72
73
  },
73
74
  endusers: {
74
- setPassword: (id: string, password: string) => Promise<void>,
75
- isAuthenticated: (id: string, authToken: string) => Promise<{ isAuthenticated: boolean, enduser: Enduser }>
76
- }
75
+ setPassword: (args: { id: string, password: string }) => Promise<void>,
76
+ isAuthenticated: (args: { id: string, authToken: string }) => Promise<{ isAuthenticated: boolean, enduser: Enduser }>
77
+ },
78
+ users: {
79
+ display_names: () => Promise<{ fname: string, lname: string, id: string }[]>,
80
+ },
81
+ files: {
82
+ prepare_file_upload: (args: { name: string, size: number, type: string }) => Promise<{ presignedUpload: S3PresignedPost, file: File }>,
83
+ file_download_URL: (args: { secureName: string }) => Promise<{ downloadURL: string }>,
84
+ },
77
85
  }
78
86
 
79
- export class Session extends SessionManager{
87
+ export class Session extends SessionManager {
80
88
  api: Queries;
81
- userInfo: UserSession;
89
+ userInfo!: UserSession;
82
90
 
83
91
  constructor(o?: SessionOptions) {
84
- super(o)
85
- this.userInfo = {} as UserSession
92
+ super({ ...o, cacheKey: o?.cacheKey || "tellescope_user" })
86
93
  const queries = loadDefaultQueries(this) as Queries
87
94
 
88
- queries.journeys.updateState = (id, name, updates) => this.PATCH(`/v1/journey/${id}/state/${name}`, { updates })
89
- queries.endusers.setPassword = (id, password) => this.POST(`/v1/set-enduser-password`, { id, password })
90
- queries.endusers.isAuthenticated = (id, authToken) => this.GET(`/v1/enduser-is-authenticated`, { id, authToken })
95
+ queries.journeys.updateState = ({id, name, updates}) => this._PATCH(`/v1/journey/${id}/state/${name}`, { updates })
96
+ queries.endusers.setPassword = ({id, password}) => this._POST(`/v1/set-enduser-password`, { id, password })
97
+ queries.endusers.isAuthenticated = ({id, authToken}) => this._GET(`/v1/enduser-is-authenticated`, { id, authToken })
98
+ queries.users.display_names = () => this._GET<{}, { fname: string, lname: string, id: string }[]>(`/v1/user-display-names`),
99
+ queries.files.prepare_file_upload = (args) => this._POST(`/v1/prepare-file-upload`, args),
100
+ queries.files.file_download_URL = a => this._GET('/v1/file-download-URL', a),
91
101
 
92
102
  this.api = queries
103
+
104
+ // if (this.userInfo) this.refresh_session()
93
105
  }
94
106
 
107
+ _POST = async <A,R=void>(endpoint: string, args?: A, authenticated=true) => {
108
+ await this.refresh_session_if_expiring_soon()
109
+ return await this.POST<A,R>(endpoint, args, authenticated)
110
+ }
95
111
 
96
- handle_new_session = async ({ authToken, ...userInfo }: UserSession & { authToken: string }) => {
97
- this.setAuthToken(authToken)
98
- this.userInfo = userInfo
112
+ _GET = async <A,R=void>(endpoint: string, params?: A, authenticated=true) => {
113
+ await this.refresh_session_if_expiring_soon()
114
+ return await this.GET<A,R>(endpoint, params, authenticated)
115
+ }
99
116
 
100
- this.socket = io(`${this.host}/${userInfo.organization}`, { transports: ['websocket'] }); // supporting polling requires sticky session at load balancer
101
- this.socket.on('disconnect', () => { this.socketAuthenticated = false })
102
- this.socket.on('authenticated', () => { this.socketAuthenticated = true })
117
+ _PATCH = async <A,R=void>(endpoint: string, params?: A, authenticated=true) => {
118
+ await this.refresh_session_if_expiring_soon()
119
+ return await this.PATCH<A,R>(endpoint, params, authenticated)
120
+ }
103
121
 
104
- this.socket.emit('authenticate', authToken)
122
+ _DELETE = async <A,R=void>(endpoint: string, args?: A, authenticated=true) => {
123
+ await this.refresh_session_if_expiring_soon()
124
+ return await this.DELETE<A,R>(endpoint, args, authenticated)
125
+ }
126
+
127
+ handle_new_session = async ({ authToken, ...userInfo }: UserSession & { authToken: string }) => {
128
+ this.sessionStart = Date.now()
129
+ this.setAuthToken(authToken)
130
+ this.setUserInfo(userInfo)
131
+ this.authenticate_socket()
105
132
 
106
133
  return { authToken, ...userInfo }
107
134
  }
108
135
 
109
136
  refresh_session = async () => {
110
- const { userInfo, authToken } = await this.GET<{}, { userInfo: UserSession } & { authToken: string }>('/refresh-session')
111
- await this.handle_new_session({ ...userInfo, authToken })
137
+ console.log('refreshing session')
138
+ const { user, authToken } = await this.POST<{}, { user: UserSession } & { authToken: string }>('/v1/refresh-session')
139
+ await this.handle_new_session({ ...user, authToken })
140
+ return { user, authToken }
141
+ }
142
+ refresh_session_if_expiring_soon = async () => {
143
+ const elapsedSessionMS = Date.now() - (this.sessionStart || Date.now())
144
+
145
+ if (this.AUTO_REFRESH_MS < elapsedSessionMS) { return await this.refresh_session()}
112
146
  }
113
147
 
114
148
  authenticate = async (email: string, password: string, url?: string) => {
@@ -118,17 +152,11 @@ export class Session extends SessionManager{
118
152
  await this.POST<{email: string, password: string }, UserSession & { authToken: string }>('/submit-login', { email, password })
119
153
  )
120
154
  }
155
+ logout = async () => {
156
+ this.clearState()
157
+ await this.POST('/logout-api').catch(console.error)
158
+ }
121
159
 
122
- subscribe = (rooms: { [index: string]: keyof ClientModelForName } ) => this.EMIT('join-rooms', { rooms })
123
-
124
- handle_events = ( handlers: { [index: string]: (a: any) => void } ) => {
125
- for (const handler in handlers) this.ON(handler, handlers[handler])
126
- }
127
-
128
- unsubscribe = (roomIds: string[]) => this.EMIT('leave-rooms', { roomIds })
129
-
130
- socket_is_authenticated = () => this.socketAuthenticated
131
- logout = () => this.POST('/logout-api')
132
160
  reset_db = () => this.POST('/reset-demo')
133
161
  test_online = () => this.GET<{}, string>('/v1')
134
162
  test_authenticated = () => this.GET<{}, string>('/v1/test-authenticated')
package/src/session.ts CHANGED
@@ -1,9 +1,19 @@
1
1
  import axios from "axios"
2
- import { Socket } from 'socket.io-client'
3
-
4
- import { } from "@tellescope/types-models"
5
- import { ClientModelForName_required, ClientModelForName_readonly, ClientModelForName_updatesDisabled } from "@tellescope/types-client"
6
- import { url_safe_path } from "@tellescope/utilities"
2
+ import NodeFormData from "form-data"
3
+ import { Socket, io } from 'socket.io-client'
4
+
5
+ import {
6
+ CustomUpdateOptions,
7
+ FileBlob,
8
+ SortOption,
9
+ S3PresignedPost,
10
+ } from "@tellescope/types-utilities"
11
+ import {
12
+ ClientModelForName,
13
+ ClientModelForName_required,
14
+ ClientModelForName_readonly,
15
+ ClientModelForName_updatesDisabled
16
+ } from "@tellescope/types-client"
7
17
 
8
18
  export const DEFAULT_HOST = 'https://api.tellescope.com'
9
19
 
@@ -11,6 +21,12 @@ export interface SessionOptions {
11
21
  apiKey?: string;
12
22
  authToken?: string;
13
23
  host?: string;
24
+ cacheKey?: string;
25
+ handleUnauthenticated?: () => Promise<void>;
26
+ }
27
+
28
+ interface RequestOptions {
29
+ refresh_session?: () => Promise<any>,
14
30
  }
15
31
 
16
32
  export type Filter<T> = { [K in keyof T]: T[K] }
@@ -31,23 +47,6 @@ export interface APIQuery<
31
47
  deleteOne: (id: string) => Promise<void>;
32
48
  }
33
49
 
34
- export const defaultQueries = <N extends keyof ClientModelForName>(
35
- s: Session, n: keyof ClientModelForName_required
36
- ): APIQuery<N> => {
37
-
38
- const safeName = url_safe_path(n)
39
- const singularName = (safeName).substring(0, safeName.length - 1)
40
-
41
- return {
42
- createOne: o => s.POST(`/v1/${singularName}`, o),
43
- createSome: os => s.POST(`/v1/${safeName}`, { create: os }),
44
- getOne: (id, filter) => s.GET(`/v1/${singularName}/${id}`, { filter }),
45
- getSome: (o, filter) => s.GET(`/v1/${safeName}`, { ...o, filter }),
46
- updateOne: (id, updates, options) => s.PATCH(`/v1/${singularName}/${id}`, { updates, options }),
47
- deleteOne: id => s.DELETE(`/v1/${singularName}/${id}`),
48
- }
49
- }
50
-
51
50
  const generateBearer = (authToken: string) => `Bearer ${authToken}`
52
51
 
53
52
  const parseError = (err: any) => {
@@ -59,32 +58,80 @@ const parseError = (err: any) => {
59
58
  return err
60
59
  }
61
60
 
61
+ const DEFAULT_AUTHTOKEN_KEY = 'tellescope_authToken'
62
+ const IN_BROWSER = typeof window !== 'undefined'
63
+ const has_local_storage = () => typeof window !== 'undefined' && !!window.localStorage
64
+ const set_cache = (key: string, authToken: string) => has_local_storage() && (window.localStorage[key] = authToken)
65
+ const access_cache = (key=DEFAULT_AUTHTOKEN_KEY) => has_local_storage() ? window.localStorage[key] : undefined
66
+
62
67
  export class Session {
63
68
  host: string;
64
69
  authToken: string;
70
+ cacheKey: string;
65
71
  apiKey?: string;
66
72
  socket?: Socket;
73
+ handleUnauthenticated?: SessionOptions['handleUnauthenticated']
67
74
  socketAuthenticated: boolean;
75
+ userInfo: { businessId?: string };
76
+ sessionStart = Date.now();
77
+ AUTO_REFRESH_MS = 3600000 // 1hr elapsed
68
78
 
69
79
  config: { headers: { Authorization: string }};
70
80
 
71
- constructor(o={} as SessionOptions) {
81
+ constructor(o={} as SessionOptions & RequestOptions) {
72
82
  this.host= o.host ?? DEFAULT_HOST
73
- this.authToken = o.authToken ?? '';
74
- this.config = { headers: { Authorization: generateBearer(o.authToken ?? '') } }
75
83
  this.apiKey = o.apiKey ?? '';
76
84
  this.socket = undefined as Socket | undefined
77
85
  this.socketAuthenticated = false
86
+ this.handleUnauthenticated = o.handleUnauthenticated
87
+
88
+ this.cacheKey = o.cacheKey || DEFAULT_AUTHTOKEN_KEY
89
+ this.authToken = o.authToken ?? access_cache(o.cacheKey) ?? '';
90
+ this.userInfo = JSON.parse(access_cache(o.cacheKey + 'userInfo') || '{}');
91
+ if (this.authToken) {
92
+ set_cache(this.cacheKey, this.authToken)
93
+ this.authenticate_socket()
94
+ }
95
+ this.config = { headers: { Authorization: generateBearer(this.authToken ?? '') } } // initialize after authToken
78
96
  }
97
+
98
+ resolve_field = async <T>(p: () => Promise<T>, field: keyof T) => (await p())[field]
79
99
 
80
100
  setAuthToken = (a: string) => {
81
101
  this.authToken = a;
82
102
  this.config.headers.Authorization = generateBearer(a);
103
+ set_cache(this.cacheKey, a)
83
104
  }
84
- // setApiKey = (k: string) => apiKey = k
85
105
 
86
- getAuthInfo = (requiresAuth?: boolean) => requiresAuth && this.apiKey ? { apiKey: this.apiKey } : { }
106
+ setUserInfo = (u: { businessId: string }) => {
107
+ this.userInfo = u;
108
+ set_cache(this.cacheKey + 'userInfo', JSON.stringify(u))
109
+ }
110
+
111
+ clearCache = () => {
112
+ set_cache(this.cacheKey, '')
113
+ set_cache(this.cacheKey + 'userInfo', '')
114
+ }
115
+
116
+ clearState = () => {
117
+ this.apiKey = ''
118
+ this.authToken = ''
119
+ this.userInfo = { }
120
+ this.clearCache()
121
+ }
87
122
 
123
+ getAuthInfo = (requiresAuth?: boolean) => requiresAuth && this.apiKey ? { apiKey: this.apiKey } : { }
124
+
125
+ errorHandler = async (_err: any) => {
126
+ const err = parseError(_err)
127
+ if (err === 'Unauthenticated') {
128
+ this.authToken = ''
129
+ this.clearCache()
130
+ await this.handleUnauthenticated?.()
131
+ }
132
+
133
+ return err
134
+ }
88
135
  POST = async <A,R=void>(endpoint: string, args?: A, authenticated=true) => {
89
136
  try {
90
137
  return (await axios.post(
@@ -92,7 +139,7 @@ export class Session {
92
139
  { ...args, ...this.getAuthInfo(authenticated) },
93
140
  this.config)
94
141
  ).data as R
95
- } catch(err) { throw parseError(err) }
142
+ } catch(err) { throw await this.errorHandler(err) }
96
143
  }
97
144
 
98
145
  GET = async <A,R=void>(endpoint: string, params?: A, authenticated=true) => {
@@ -102,7 +149,7 @@ export class Session {
102
149
  { params: { ...params, ...this.getAuthInfo(authenticated) },
103
150
  headers: this.config.headers })
104
151
  ).data as R
105
- } catch(err) { throw parseError(err) }
152
+ } catch(err) { throw await this.errorHandler(err) }
106
153
  }
107
154
 
108
155
  PATCH = async <A,R=void>(endpoint: string, params?: A, authenticated=true) => {
@@ -112,7 +159,7 @@ export class Session {
112
159
  { ...params, ...this.getAuthInfo(authenticated) },
113
160
  this.config)
114
161
  ).data as R
115
- } catch(err) { throw parseError(err) }
162
+ } catch(err) { throw await this.errorHandler(err) }
116
163
  }
117
164
 
118
165
  DELETE = async <A,R=void>(endpoint: string, args?: A, authenticated=true) => {
@@ -122,14 +169,55 @@ export class Session {
122
169
  { data: { ...args, ...this.getAuthInfo(authenticated) },
123
170
  headers: this.config.headers })
124
171
  ).data as R
125
- } catch(err) { throw parseError(err) }
172
+ } catch(err) { throw await this.errorHandler(err) }
173
+ }
174
+
175
+ UPLOAD = async (presigned: S3PresignedPost, file?: FileBlob, buffer?: FileBlob) => {
176
+ const formData = IN_BROWSER ? new FormData() : new NodeFormData();
177
+ Object.keys(presigned.fields).forEach(key => {
178
+ formData.append(key, presigned.fields[key]);
179
+ });
180
+
181
+ // has to be appended last
182
+ if (!IN_BROWSER) file = buffer || file // use raw buffer if file is Node Blob
183
+ formData.append("file", file as Blob)
184
+
185
+ try {
186
+ await axios.post(presigned.url, formData, {
187
+ headers: IN_BROWSER
188
+ ? { 'Content-Type': 'multipart/form-data'}
189
+ : {
190
+ ...(formData as NodeFormData).getHeaders(),
191
+ 'Content-Length' : (formData as any).maxDataSize
192
+ }
193
+ })
194
+ }
195
+ catch(err) { console.error(err); throw err }
126
196
  }
127
197
 
128
- EMIT = async (route: string, args: object, authenticated=true) => {
198
+ EMIT = async (route: string, args: object, authenticated=true, options={} as RequestOptions) => {
129
199
  this.socket?.emit(route, { ...args, ...authenticated ? { authToken: this.authToken } : {} } )
130
200
  }
131
201
 
132
202
  ON = <T={}>(s: string, callback: (a: T) => void) => this.socket?.on(s, callback)
133
203
 
134
- authenticate_socket = () => !!this.socket?.emit('authenticate', this.authToken)
204
+ subscribe = (rooms: { [index: string]: keyof ClientModelForName }, handlers?: { [index: string]: (a: any) => void } ) => {
205
+ if (handlers) { this.handle_events(handlers) }
206
+ this.EMIT('join-rooms', { rooms })
207
+ }
208
+
209
+ handle_events = ( handlers: { [index: string]: (a: any) => void } ) => {
210
+ for (const handler in handlers) this.ON(handler, handlers[handler])
211
+ }
212
+
213
+ unsubscribe = (roomIds: string[]) => this.EMIT('leave-rooms', { roomIds })
214
+ removeAllSocketListeners = (s: string) => this.socket?.removeAllListeners(s)
215
+
216
+ authenticate_socket = () => {
217
+ this.socket = io(`${this.host}/${this.userInfo.businessId}`, { transports: ['websocket'] }); // supporting polling requires sticky session at load balancer
218
+ this.socket.on('disconnect', () => { this.socketAuthenticated = false })
219
+ this.socket.on('authenticated', () => { this.socketAuthenticated = true })
220
+
221
+ this.socket.emit('authenticate', this.authToken)
222
+ }
135
223
  }
@@ -2,9 +2,11 @@ import {
2
2
  assert,
3
3
  async_test,
4
4
  log_header,
5
- objects_equivalent,
6
5
  wait,
7
6
  } from "@tellescope/testing"
7
+ import {
8
+ objects_equivalent,
9
+ } from "@tellescope/utilities"
8
10
  import { Session, /* APIQuery */ } from "../sdk"
9
11
  import { PublicEndpoints } from "../public"
10
12
 
@@ -30,14 +32,14 @@ const enduser_login_tests = async () => {
30
32
  )
31
33
  await async_test(
32
34
  'setPassword',
33
- () => sdk.api.endusers.setPassword(e.id, password),
35
+ () => sdk.api.endusers.setPassword({ id: e.id, password }),
34
36
  { onResult: _ => true }
35
37
  )
36
38
 
37
39
  let authToken = 'placeholder'
38
40
  await async_test(
39
41
  'isAuthenticated (no)',
40
- () => sdk.api.endusers.isAuthenticated(e.id, authToken),
42
+ () => sdk.api.endusers.isAuthenticated({ id: e.id, authToken }),
41
43
  { onResult: ({ isAuthenticated, enduser }) => isAuthenticated === false && enduser === null }
42
44
  )
43
45
  await async_test(
@@ -47,7 +49,7 @@ const enduser_login_tests = async () => {
47
49
  )
48
50
  await async_test(
49
51
  'isAuthenticated (yes)',
50
- () => sdk.api.endusers.isAuthenticated(e.id, authToken),
52
+ () => sdk.api.endusers.isAuthenticated({ id: e.id, authToken }),
51
53
  { onResult: ({ isAuthenticated, enduser }) => isAuthenticated === true && enduser?.id === e.id }
52
54
  )
53
55
  }
@@ -1,9 +1,18 @@
1
1
  import {
2
2
  assert,
3
+ async_test,
3
4
  log_header,
4
- objects_equivalent,
5
5
  wait,
6
6
  } from "@tellescope/testing"
7
+ import {
8
+ objects_equivalent,
9
+ } from "@tellescope/utilities"
10
+ import {
11
+ ChatMessage,
12
+ } from "@tellescope/types-client"
13
+ import {
14
+ Indexable,
15
+ } from "@tellescope/types-utilities"
7
16
  import { EnduserSession } from "../enduser"
8
17
  import { Session, /* APIQuery */ } from "../sdk"
9
18
 
@@ -23,8 +32,7 @@ if (!(email && password && email2 && password2)) {
23
32
  const basic_tests = async () => {
24
33
  const socket_events: Indexable[] = []
25
34
 
26
- user2.subscribe({ 'endusers': 'endusers' })
27
- user2.handle_events({
35
+ user2.subscribe({ 'endusers': 'endusers' }, {
28
36
  'created-endusers': es => socket_events.push(es),
29
37
  'updated-endusers': es => socket_events.push(es),
30
38
  'deleted-endusers': es => socket_events.push(es),
@@ -116,11 +124,10 @@ const access_tests = async () => {
116
124
  }
117
125
 
118
126
  const enduser_tests = async () => {
119
- const enduser = await user1.api.endusers.createOne({ email: "sockettest@tellescope.com" })
120
- await user1.api.endusers.setPassword(enduser.id, 'enduserpassword')
127
+ const enduser = await user1.api.endusers.createOne({ email: "enduser@tellescope.com" })
128
+ await user1.api.endusers.setPassword({ id: enduser.id, password: 'enduserPassword!' })
121
129
 
122
- await enduserSDK.authenticate(enduser.email as string, 'enduserpassword')
123
- enduserSDK.authenticate_socket()
130
+ await enduserSDK.authenticate(enduser.email as string, 'enduserPassword!')
124
131
  await wait(undefined, 25)
125
132
 
126
133
  const userEvents = [] as ChatMessage[]
@@ -140,18 +147,31 @@ const enduser_tests = async () => {
140
147
 
141
148
  user1.handle_events({
142
149
  'created-chats': rs => userEvents.push(...rs),
143
- 'updated-chats': rs => enduserEvents.push(...rs),
150
+ })
151
+ enduserSDK.handle_events({
152
+ 'created-chats': rs => enduserEvents.push(...rs),
144
153
  })
145
154
 
146
155
  const messageToEnduser = await user1.api.chats.createOne({ roomId: room.id, message: "Hello!" })
147
156
  const messageToUser = await enduserSDK.api.chats.createOne({ roomId: room.id, message: "Hello right back!" })
157
+ await wait(undefined, 25)
148
158
 
149
- console.log(userEvents, enduserEvents)
159
+ assert(objects_equivalent(userEvents[0], messageToUser), 'no message on socket', 'push message to user')
160
+ assert(objects_equivalent(enduserEvents[0], messageToEnduser), 'no message on socket', 'push message to enduser')
161
+
162
+ // test enduser logout
163
+ await enduserSDK.api.endusers.logout()
164
+ await async_test(
165
+ `verify enduser logout works`,
166
+ () => enduserSDK.api.chats.getSome({}),
167
+ { shouldError: true, onError: (e: string) => e === 'Unauthenticated' }
168
+ )
150
169
 
170
+ // keep these models around for front-end testing
151
171
  // cleanup
152
- await user1.api.endusers.deleteOne(enduser.id)
153
- await user1.api.chats.deleteOne(messageToEnduser.id)
154
- await user1.api.chats.deleteOne(messageToUser.id)
172
+ // await user1.api.endusers.deleteOne(enduser.id)
173
+ // await user1.api.chats.deleteOne(messageToEnduser.id)
174
+ // await user1.api.chats.deleteOne(messageToUser.id)
155
175
  }
156
176
 
157
177
  (async () => {
@@ -160,10 +180,11 @@ const enduser_tests = async () => {
160
180
  try {
161
181
  await user1.authenticate(email, password, host)
162
182
  await user1.reset_db()
163
- await user2.authenticate(email2, password2, host) // generate authToken + socket connection for API key
164
-
183
+ await user2.authenticate(email2, password2, host) // generate authToken + socket connection for API keyj
184
+ await wait(undefined, 25)
185
+
165
186
  let loopCount = 0
166
- while (!(user1.socket_is_authenticated() && user2.socket_is_authenticated()) && ++loopCount < 10) {
187
+ while (!(user1.socketAuthenticated && user2.socketAuthenticated) && ++loopCount < 10) {
167
188
  user2.authenticate_socket()
168
189
  await wait(undefined, 100)
169
190
  }
@@ -171,7 +192,7 @@ const enduser_tests = async () => {
171
192
  console.log("Failed to authenticate")
172
193
  process.exit()
173
194
  }
174
-
195
+
175
196
  await enduser_tests()
176
197
  await basic_tests()
177
198
  await access_tests()