@ethora/sdk-backend 25.12.20 → 26.2.1
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/README.md +120 -108
- package/dist/repositories/EthoraSDKService.d.ts +30 -18
- package/dist/repositories/EthoraSDKService.d.ts.map +1 -1
- package/dist/repositories/EthoraSDKService.js +159 -84
- package/dist/repositories/EthoraSDKService.js.map +1 -1
- package/dist/scripts/test-logs.d.ts +2 -0
- package/dist/scripts/test-logs.d.ts.map +1 -0
- package/dist/scripts/test-logs.js +93 -0
- package/dist/scripts/test-logs.js.map +1 -0
- package/dist/types/index.d.ts +14 -7
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -27,6 +27,15 @@ This guide will walk you through integrating the Ethora SDK into your existing N
|
|
|
27
27
|
- `ETHORA_CHAT_APP_SECRET`
|
|
28
28
|
- `ETHORA_CHAT_BOT_JID` (optional, for chatbot features)
|
|
29
29
|
|
|
30
|
+
## API Documentation (Swagger)
|
|
31
|
+
|
|
32
|
+
Ethora exposes Swagger UI from every running backend instance at:
|
|
33
|
+
|
|
34
|
+
- **Hosted (Ethora main)**: `https://api.ethoradev.com/api-docs/`
|
|
35
|
+
- **Enterprise/self-hosted**: `https://api.<your-domain>/api-docs/`
|
|
36
|
+
|
|
37
|
+
If you are running a separate staging instance, the same pattern applies (e.g. `https://api.asterotoken.com/api-docs/`).
|
|
38
|
+
|
|
30
39
|
## Installation
|
|
31
40
|
|
|
32
41
|
### Step 1: Install the Package
|
|
@@ -70,7 +79,7 @@ npm install dotenv
|
|
|
70
79
|
In your main application file (e.g., `app.js`, `server.js`, or `index.ts`):
|
|
71
80
|
|
|
72
81
|
```typescript
|
|
73
|
-
import dotenv from
|
|
82
|
+
import dotenv from 'dotenv';
|
|
74
83
|
|
|
75
84
|
// Load environment variables
|
|
76
85
|
dotenv.config();
|
|
@@ -81,7 +90,7 @@ dotenv.config();
|
|
|
81
90
|
### Step 1: Import the SDK
|
|
82
91
|
|
|
83
92
|
```typescript
|
|
84
|
-
import { getEthoraSDKService } from
|
|
93
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
85
94
|
```
|
|
86
95
|
|
|
87
96
|
### Step 2: Initialize the Service
|
|
@@ -92,7 +101,7 @@ You can initialize the service in several ways:
|
|
|
92
101
|
|
|
93
102
|
```typescript
|
|
94
103
|
// services/chatService.ts
|
|
95
|
-
import { getEthoraSDKService } from
|
|
104
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
96
105
|
|
|
97
106
|
// Get the singleton instance
|
|
98
107
|
const chatService = getEthoraSDKService();
|
|
@@ -104,7 +113,7 @@ export default chatService;
|
|
|
104
113
|
|
|
105
114
|
```typescript
|
|
106
115
|
// In your route handler or service
|
|
107
|
-
import { getEthoraSDKService } from
|
|
116
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
108
117
|
|
|
109
118
|
const chatService = getEthoraSDKService();
|
|
110
119
|
```
|
|
@@ -113,8 +122,8 @@ const chatService = getEthoraSDKService();
|
|
|
113
122
|
|
|
114
123
|
```typescript
|
|
115
124
|
// chat.service.ts
|
|
116
|
-
import { Injectable } from
|
|
117
|
-
import { getEthoraSDKService } from
|
|
125
|
+
import { Injectable } from '@nestjs/common';
|
|
126
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
118
127
|
|
|
119
128
|
@Injectable()
|
|
120
129
|
export class ChatService {
|
|
@@ -130,16 +139,16 @@ export class ChatService {
|
|
|
130
139
|
|
|
131
140
|
```typescript
|
|
132
141
|
// routes/chat.ts
|
|
133
|
-
import express, { Request, Response } from
|
|
134
|
-
import { getEthoraSDKService } from
|
|
135
|
-
import axios from
|
|
142
|
+
import express, { Request, Response } from 'express';
|
|
143
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
144
|
+
import axios from 'axios';
|
|
136
145
|
|
|
137
146
|
const router = express.Router();
|
|
138
147
|
const chatService = getEthoraSDKService();
|
|
139
148
|
|
|
140
149
|
// Create a chat room for a workspace
|
|
141
150
|
router.post(
|
|
142
|
-
|
|
151
|
+
'/workspaces/:workspaceId/chat',
|
|
143
152
|
async (req: Request, res: Response) => {
|
|
144
153
|
try {
|
|
145
154
|
const { workspaceId } = req.params;
|
|
@@ -148,7 +157,7 @@ router.post(
|
|
|
148
157
|
const response = await chatService.createChatRoom(workspaceId, {
|
|
149
158
|
title: roomData.title || `Chat Room ${workspaceId}`,
|
|
150
159
|
uuid: workspaceId,
|
|
151
|
-
type: roomData.type ||
|
|
160
|
+
type: roomData.type || 'group',
|
|
152
161
|
...roomData,
|
|
153
162
|
});
|
|
154
163
|
|
|
@@ -156,18 +165,18 @@ router.post(
|
|
|
156
165
|
} catch (error) {
|
|
157
166
|
if (axios.isAxiosError(error)) {
|
|
158
167
|
res.status(error.response?.status || 500).json({
|
|
159
|
-
error:
|
|
168
|
+
error: 'Failed to create chat room',
|
|
160
169
|
details: error.response?.data,
|
|
161
170
|
});
|
|
162
171
|
} else {
|
|
163
|
-
res.status(500).json({ error:
|
|
172
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
|
-
}
|
|
175
|
+
},
|
|
167
176
|
);
|
|
168
177
|
|
|
169
178
|
// Create a user
|
|
170
|
-
router.post(
|
|
179
|
+
router.post('/users/:userId', async (req: Request, res: Response) => {
|
|
171
180
|
try {
|
|
172
181
|
const { userId } = req.params;
|
|
173
182
|
const userData = req.body;
|
|
@@ -177,50 +186,50 @@ router.post("/users/:userId", async (req: Request, res: Response) => {
|
|
|
177
186
|
} catch (error) {
|
|
178
187
|
if (axios.isAxiosError(error)) {
|
|
179
188
|
res.status(error.response?.status || 500).json({
|
|
180
|
-
error:
|
|
189
|
+
error: 'Failed to create user',
|
|
181
190
|
details: error.response?.data,
|
|
182
191
|
});
|
|
183
192
|
} else {
|
|
184
|
-
res.status(500).json({ error:
|
|
193
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
185
194
|
}
|
|
186
195
|
}
|
|
187
196
|
});
|
|
188
197
|
|
|
189
198
|
// Grant user access to chat room
|
|
190
199
|
router.post(
|
|
191
|
-
|
|
200
|
+
'/workspaces/:workspaceId/chat/users/:userId',
|
|
192
201
|
async (req: Request, res: Response) => {
|
|
193
202
|
try {
|
|
194
203
|
const { workspaceId, userId } = req.params;
|
|
195
204
|
|
|
196
205
|
await chatService.grantUserAccessToChatRoom(workspaceId, userId);
|
|
197
|
-
res.json({ success: true, message:
|
|
206
|
+
res.json({ success: true, message: 'Access granted' });
|
|
198
207
|
} catch (error) {
|
|
199
208
|
if (axios.isAxiosError(error)) {
|
|
200
209
|
res.status(error.response?.status || 500).json({
|
|
201
|
-
error:
|
|
210
|
+
error: 'Failed to grant access',
|
|
202
211
|
details: error.response?.data,
|
|
203
212
|
});
|
|
204
213
|
} else {
|
|
205
|
-
res.status(500).json({ error:
|
|
214
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
|
-
}
|
|
217
|
+
},
|
|
209
218
|
);
|
|
210
219
|
|
|
211
220
|
// Generate client JWT token
|
|
212
|
-
router.get(
|
|
221
|
+
router.get('/users/:userId/chat-token', (req: Request, res: Response) => {
|
|
213
222
|
try {
|
|
214
223
|
const { userId } = req.params;
|
|
215
224
|
const token = chatService.createChatUserJwtToken(userId);
|
|
216
225
|
res.json({ token });
|
|
217
226
|
} catch (error) {
|
|
218
|
-
res.status(500).json({ error:
|
|
227
|
+
res.status(500).json({ error: 'Failed to generate token' });
|
|
219
228
|
}
|
|
220
229
|
});
|
|
221
230
|
|
|
222
231
|
// Get users
|
|
223
|
-
router.get(
|
|
232
|
+
router.get('/users', async (req: Request, res: Response) => {
|
|
224
233
|
try {
|
|
225
234
|
const { chatName, xmppUsername } = req.query;
|
|
226
235
|
const params: any = {};
|
|
@@ -228,32 +237,32 @@ router.get("/users", async (req: Request, res: Response) => {
|
|
|
228
237
|
if (xmppUsername) params.xmppUsername = String(xmppUsername);
|
|
229
238
|
|
|
230
239
|
const response = await chatService.getUsers(
|
|
231
|
-
Object.keys(params).length > 0 ? params : undefined
|
|
240
|
+
Object.keys(params).length > 0 ? params : undefined,
|
|
232
241
|
);
|
|
233
242
|
res.json({ success: true, data: response });
|
|
234
243
|
} catch (error) {
|
|
235
244
|
if (axios.isAxiosError(error)) {
|
|
236
245
|
res.status(error.response?.status || 500).json({
|
|
237
|
-
error:
|
|
246
|
+
error: 'Failed to get users',
|
|
238
247
|
details: error.response?.data,
|
|
239
248
|
});
|
|
240
249
|
} else {
|
|
241
|
-
res.status(500).json({ error:
|
|
250
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
242
251
|
}
|
|
243
252
|
}
|
|
244
253
|
});
|
|
245
254
|
|
|
246
255
|
// Update users (batch)
|
|
247
|
-
router.patch(
|
|
256
|
+
router.patch('/users', async (req: Request, res: Response) => {
|
|
248
257
|
try {
|
|
249
258
|
const { users } = req.body;
|
|
250
259
|
if (!Array.isArray(users) || users.length === 0) {
|
|
251
|
-
return res.status(400).json({ error:
|
|
260
|
+
return res.status(400).json({ error: 'users must be a non-empty array' });
|
|
252
261
|
}
|
|
253
262
|
if (users.length > 100) {
|
|
254
263
|
return res
|
|
255
264
|
.status(400)
|
|
256
|
-
.json({ error:
|
|
265
|
+
.json({ error: 'Maximum 100 users allowed per request' });
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
const response = await chatService.updateUsers(users);
|
|
@@ -261,11 +270,11 @@ router.patch("/users", async (req: Request, res: Response) => {
|
|
|
261
270
|
} catch (error) {
|
|
262
271
|
if (axios.isAxiosError(error)) {
|
|
263
272
|
res.status(error.response?.status || 500).json({
|
|
264
|
-
error:
|
|
273
|
+
error: 'Failed to update users',
|
|
265
274
|
details: error.response?.data,
|
|
266
275
|
});
|
|
267
276
|
} else {
|
|
268
|
-
res.status(500).json({ error:
|
|
277
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
});
|
|
@@ -277,9 +286,9 @@ export default router;
|
|
|
277
286
|
|
|
278
287
|
```typescript
|
|
279
288
|
// chat/chat.service.ts
|
|
280
|
-
import { Injectable, HttpException, HttpStatus } from
|
|
281
|
-
import { getEthoraSDKService } from
|
|
282
|
-
import axios from
|
|
289
|
+
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
|
290
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
291
|
+
import axios from 'axios';
|
|
283
292
|
|
|
284
293
|
@Injectable()
|
|
285
294
|
export class ChatService {
|
|
@@ -292,10 +301,10 @@ export class ChatService {
|
|
|
292
301
|
if (axios.isAxiosError(error)) {
|
|
293
302
|
throw new HttpException(
|
|
294
303
|
{
|
|
295
|
-
message:
|
|
304
|
+
message: 'Failed to create chat room',
|
|
296
305
|
details: error.response?.data,
|
|
297
306
|
},
|
|
298
|
-
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
307
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
299
308
|
);
|
|
300
309
|
}
|
|
301
310
|
throw error;
|
|
@@ -309,10 +318,10 @@ export class ChatService {
|
|
|
309
318
|
if (axios.isAxiosError(error)) {
|
|
310
319
|
throw new HttpException(
|
|
311
320
|
{
|
|
312
|
-
message:
|
|
321
|
+
message: 'Failed to create user',
|
|
313
322
|
details: error.response?.data,
|
|
314
323
|
},
|
|
315
|
-
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
324
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
316
325
|
);
|
|
317
326
|
}
|
|
318
327
|
throw error;
|
|
@@ -325,28 +334,28 @@ export class ChatService {
|
|
|
325
334
|
}
|
|
326
335
|
|
|
327
336
|
// chat/chat.controller.ts
|
|
328
|
-
import { Controller, Post, Get, Param, Body } from
|
|
329
|
-
import { ChatService } from
|
|
337
|
+
import { Controller, Post, Get, Param, Body } from '@nestjs/common';
|
|
338
|
+
import { ChatService } from './chat.service';
|
|
330
339
|
|
|
331
|
-
@Controller(
|
|
340
|
+
@Controller('chat')
|
|
332
341
|
export class ChatController {
|
|
333
342
|
constructor(private readonly chatService: ChatService) {}
|
|
334
343
|
|
|
335
|
-
@Post(
|
|
344
|
+
@Post('workspaces/:workspaceId/rooms')
|
|
336
345
|
async createChatRoom(
|
|
337
|
-
@Param(
|
|
338
|
-
@Body() roomData: any
|
|
346
|
+
@Param('workspaceId') workspaceId: string,
|
|
347
|
+
@Body() roomData: any,
|
|
339
348
|
) {
|
|
340
349
|
return this.chatService.createChatRoom(workspaceId, roomData);
|
|
341
350
|
}
|
|
342
351
|
|
|
343
|
-
@Post(
|
|
344
|
-
async createUser(@Param(
|
|
352
|
+
@Post('users/:userId')
|
|
353
|
+
async createUser(@Param('userId') userId: string, @Body() userData: any) {
|
|
345
354
|
return this.chatService.createUser(userId, userData);
|
|
346
355
|
}
|
|
347
356
|
|
|
348
|
-
@Get(
|
|
349
|
-
getClientToken(@Param(
|
|
357
|
+
@Get('users/:userId/token')
|
|
358
|
+
getClientToken(@Param('userId') userId: string) {
|
|
350
359
|
return { token: this.chatService.generateClientToken(userId) };
|
|
351
360
|
}
|
|
352
361
|
}
|
|
@@ -356,15 +365,15 @@ export class ChatController {
|
|
|
356
365
|
|
|
357
366
|
```typescript
|
|
358
367
|
// routes/chat.ts
|
|
359
|
-
import { FastifyInstance, FastifyRequest, FastifyReply } from
|
|
360
|
-
import { getEthoraSDKService } from
|
|
368
|
+
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
369
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
361
370
|
|
|
362
371
|
const chatService = getEthoraSDKService();
|
|
363
372
|
|
|
364
373
|
export async function chatRoutes(fastify: FastifyInstance) {
|
|
365
374
|
// Create chat room
|
|
366
375
|
fastify.post(
|
|
367
|
-
|
|
376
|
+
'/workspaces/:workspaceId/chat',
|
|
368
377
|
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
369
378
|
const { workspaceId } = request.params as { workspaceId: string };
|
|
370
379
|
const roomData = request.body as any;
|
|
@@ -372,23 +381,23 @@ export async function chatRoutes(fastify: FastifyInstance) {
|
|
|
372
381
|
try {
|
|
373
382
|
const response = await chatService.createChatRoom(
|
|
374
383
|
workspaceId,
|
|
375
|
-
roomData
|
|
384
|
+
roomData,
|
|
376
385
|
);
|
|
377
386
|
return { success: true, data: response };
|
|
378
387
|
} catch (error) {
|
|
379
|
-
reply.code(500).send({ error:
|
|
388
|
+
reply.code(500).send({ error: 'Failed to create chat room' });
|
|
380
389
|
}
|
|
381
|
-
}
|
|
390
|
+
},
|
|
382
391
|
);
|
|
383
392
|
|
|
384
393
|
// Generate client token
|
|
385
394
|
fastify.get(
|
|
386
|
-
|
|
395
|
+
'/users/:userId/chat-token',
|
|
387
396
|
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
388
397
|
const { userId } = request.params as { userId: string };
|
|
389
398
|
const token = chatService.createChatUserJwtToken(userId);
|
|
390
399
|
return { token };
|
|
391
|
-
}
|
|
400
|
+
},
|
|
392
401
|
);
|
|
393
402
|
}
|
|
394
403
|
```
|
|
@@ -403,7 +412,7 @@ When creating a new workspace, set up the chat room and initial users:
|
|
|
403
412
|
async function setupWorkspaceChat(
|
|
404
413
|
workspaceId: string,
|
|
405
414
|
userIds: string[],
|
|
406
|
-
adminUserId: string
|
|
415
|
+
adminUserId: string,
|
|
407
416
|
) {
|
|
408
417
|
const chatService = getEthoraSDKService();
|
|
409
418
|
|
|
@@ -412,15 +421,15 @@ async function setupWorkspaceChat(
|
|
|
412
421
|
await chatService.createChatRoom(workspaceId, {
|
|
413
422
|
title: `Workspace ${workspaceId}`,
|
|
414
423
|
uuid: workspaceId,
|
|
415
|
-
type:
|
|
424
|
+
type: 'group',
|
|
416
425
|
});
|
|
417
426
|
|
|
418
427
|
// 2. Create users (if they don't exist)
|
|
419
428
|
for (const userId of userIds) {
|
|
420
429
|
try {
|
|
421
430
|
await chatService.createUser(userId, {
|
|
422
|
-
firstName:
|
|
423
|
-
lastName:
|
|
431
|
+
firstName: 'User',
|
|
432
|
+
lastName: 'Name',
|
|
424
433
|
});
|
|
425
434
|
} catch (error) {
|
|
426
435
|
// User might already exist, continue
|
|
@@ -435,12 +444,12 @@ async function setupWorkspaceChat(
|
|
|
435
444
|
try {
|
|
436
445
|
await chatService.grantChatbotAccessToChatRoom(workspaceId);
|
|
437
446
|
} catch (error) {
|
|
438
|
-
console.warn(
|
|
447
|
+
console.warn('Chatbot access not configured or failed');
|
|
439
448
|
}
|
|
440
449
|
|
|
441
450
|
return { success: true };
|
|
442
451
|
} catch (error) {
|
|
443
|
-
console.error(
|
|
452
|
+
console.error('Failed to setup workspace chat:', error);
|
|
444
453
|
throw error;
|
|
445
454
|
}
|
|
446
455
|
}
|
|
@@ -453,7 +462,7 @@ When a new user joins your platform:
|
|
|
453
462
|
```typescript
|
|
454
463
|
async function onboardNewUser(
|
|
455
464
|
userId: string,
|
|
456
|
-
userData: { firstName: string; lastName: string; email: string }
|
|
465
|
+
userData: { firstName: string; lastName: string; email: string },
|
|
457
466
|
) {
|
|
458
467
|
const chatService = getEthoraSDKService();
|
|
459
468
|
|
|
@@ -474,7 +483,7 @@ async function onboardNewUser(
|
|
|
474
483
|
chatToken: clientToken,
|
|
475
484
|
};
|
|
476
485
|
} catch (error) {
|
|
477
|
-
console.error(
|
|
486
|
+
console.error('Failed to onboard user:', error);
|
|
478
487
|
throw error;
|
|
479
488
|
}
|
|
480
489
|
}
|
|
@@ -501,7 +510,7 @@ async function addUserToWorkspace(workspaceId: string, userId: string) {
|
|
|
501
510
|
|
|
502
511
|
return { success: true };
|
|
503
512
|
} catch (error) {
|
|
504
|
-
console.error(
|
|
513
|
+
console.error('Failed to add user to workspace:', error);
|
|
505
514
|
throw error;
|
|
506
515
|
}
|
|
507
516
|
}
|
|
@@ -524,13 +533,13 @@ async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
|
|
|
524
533
|
try {
|
|
525
534
|
await chatService.deleteUsers(userIds);
|
|
526
535
|
} catch (error) {
|
|
527
|
-
console.warn(
|
|
536
|
+
console.warn('Some users might not exist:', error);
|
|
528
537
|
}
|
|
529
538
|
}
|
|
530
539
|
|
|
531
540
|
return { success: true };
|
|
532
541
|
} catch (error) {
|
|
533
|
-
console.error(
|
|
542
|
+
console.error('Failed to cleanup workspace chat:', error);
|
|
534
543
|
throw error;
|
|
535
544
|
}
|
|
536
545
|
}
|
|
@@ -551,17 +560,17 @@ async function getUsersExample() {
|
|
|
551
560
|
|
|
552
561
|
// Get users by chat name (group chat)
|
|
553
562
|
const groupChatUsers = await chatService.getUsers({
|
|
554
|
-
chatName:
|
|
563
|
+
chatName: 'appId_workspaceId',
|
|
555
564
|
});
|
|
556
565
|
|
|
557
566
|
// Get users by chat name (1-on-1 chat)
|
|
558
567
|
const oneOnOneUsers = await chatService.getUsers({
|
|
559
|
-
chatName:
|
|
568
|
+
chatName: 'userA-userB',
|
|
560
569
|
});
|
|
561
570
|
|
|
562
571
|
return { allUsers, groupChatUsers, oneOnOneUsers };
|
|
563
572
|
} catch (error) {
|
|
564
|
-
console.error(
|
|
573
|
+
console.error('Failed to get users:', error);
|
|
565
574
|
throw error;
|
|
566
575
|
}
|
|
567
576
|
}
|
|
@@ -579,34 +588,34 @@ async function updateUsersExample() {
|
|
|
579
588
|
// Update multiple users (1-100 users per request)
|
|
580
589
|
const response = await chatService.updateUsers([
|
|
581
590
|
{
|
|
582
|
-
xmppUsername:
|
|
583
|
-
firstName:
|
|
584
|
-
lastName:
|
|
585
|
-
username:
|
|
586
|
-
profileImage:
|
|
591
|
+
xmppUsername: 'appId_user1',
|
|
592
|
+
firstName: 'John',
|
|
593
|
+
lastName: 'Doe',
|
|
594
|
+
username: 'johndoe',
|
|
595
|
+
profileImage: 'https://example.com/avatar1.jpg',
|
|
587
596
|
},
|
|
588
597
|
{
|
|
589
|
-
xmppUsername:
|
|
590
|
-
firstName:
|
|
591
|
-
lastName:
|
|
592
|
-
username:
|
|
598
|
+
xmppUsername: 'appId_user2',
|
|
599
|
+
firstName: 'Jane',
|
|
600
|
+
lastName: 'Smith',
|
|
601
|
+
username: 'janesmith',
|
|
593
602
|
},
|
|
594
603
|
]);
|
|
595
604
|
|
|
596
605
|
// Check results
|
|
597
606
|
response.results?.forEach((result: any) => {
|
|
598
|
-
if (result.status ===
|
|
607
|
+
if (result.status === 'updated') {
|
|
599
608
|
console.log(`User ${result.xmppUsername} updated successfully`);
|
|
600
|
-
} else if (result.status ===
|
|
609
|
+
} else if (result.status === 'not-found') {
|
|
601
610
|
console.warn(`User ${result.xmppUsername} not found`);
|
|
602
|
-
} else if (result.status ===
|
|
611
|
+
} else if (result.status === 'skipped') {
|
|
603
612
|
console.log(`User ${result.xmppUsername} update skipped`);
|
|
604
613
|
}
|
|
605
614
|
});
|
|
606
615
|
|
|
607
616
|
return response;
|
|
608
617
|
} catch (error) {
|
|
609
|
-
console.error(
|
|
618
|
+
console.error('Failed to update users:', error);
|
|
610
619
|
throw error;
|
|
611
620
|
}
|
|
612
621
|
}
|
|
@@ -619,8 +628,8 @@ async function updateUsersExample() {
|
|
|
619
628
|
The SDK uses Axios for HTTP requests, so errors are AxiosError instances:
|
|
620
629
|
|
|
621
630
|
```typescript
|
|
622
|
-
import axios from
|
|
623
|
-
import { getEthoraSDKService } from
|
|
631
|
+
import axios from 'axios';
|
|
632
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
624
633
|
|
|
625
634
|
const chatService = getEthoraSDKService();
|
|
626
635
|
|
|
@@ -635,20 +644,20 @@ async function createChatRoomSafely(workspaceId: string) {
|
|
|
635
644
|
// Handle specific error cases
|
|
636
645
|
if (status === 422) {
|
|
637
646
|
// Validation error
|
|
638
|
-
console.error(
|
|
647
|
+
console.error('Validation error:', errorData);
|
|
639
648
|
} else if (status === 401) {
|
|
640
649
|
// Authentication error
|
|
641
|
-
console.error(
|
|
650
|
+
console.error('Authentication failed - check your credentials');
|
|
642
651
|
} else if (status === 404) {
|
|
643
652
|
// Resource not found
|
|
644
|
-
console.error(
|
|
653
|
+
console.error('Resource not found');
|
|
645
654
|
} else {
|
|
646
655
|
// Other HTTP errors
|
|
647
656
|
console.error(`HTTP error ${status}:`, errorData);
|
|
648
657
|
}
|
|
649
658
|
} else {
|
|
650
659
|
// Non-HTTP errors
|
|
651
|
-
console.error(
|
|
660
|
+
console.error('Unexpected error:', error);
|
|
652
661
|
}
|
|
653
662
|
throw error;
|
|
654
663
|
}
|
|
@@ -669,17 +678,17 @@ async function ensureChatRoomExists(workspaceId: string) {
|
|
|
669
678
|
if (axios.isAxiosError(error)) {
|
|
670
679
|
const errorData = error.response?.data;
|
|
671
680
|
const errorMessage =
|
|
672
|
-
typeof errorData ===
|
|
673
|
-
? (errorData as { error?: string }).error ||
|
|
674
|
-
: String(errorData ||
|
|
681
|
+
typeof errorData === 'object' && errorData !== null
|
|
682
|
+
? (errorData as { error?: string }).error || ''
|
|
683
|
+
: String(errorData || '');
|
|
675
684
|
|
|
676
685
|
// If room already exists, that's okay
|
|
677
686
|
if (
|
|
678
687
|
error.response?.status === 422 &&
|
|
679
|
-
(errorMessage.includes(
|
|
680
|
-
errorMessage.includes(
|
|
688
|
+
(errorMessage.includes('already exist') ||
|
|
689
|
+
errorMessage.includes('already exists'))
|
|
681
690
|
) {
|
|
682
|
-
console.log(
|
|
691
|
+
console.log('Chat room already exists, continuing...');
|
|
683
692
|
return; // Success - room exists
|
|
684
693
|
}
|
|
685
694
|
}
|
|
@@ -710,8 +719,8 @@ Create a service wrapper in your application:
|
|
|
710
719
|
|
|
711
720
|
```typescript
|
|
712
721
|
// services/chatService.ts
|
|
713
|
-
import { getEthoraSDKService } from
|
|
714
|
-
import type { ChatRepository } from
|
|
722
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
723
|
+
import type { ChatRepository } from '@ethora/sdk-backend';
|
|
715
724
|
|
|
716
725
|
class ChatServiceWrapper {
|
|
717
726
|
private service: ChatRepository;
|
|
@@ -743,16 +752,16 @@ Validate environment variables on application startup:
|
|
|
743
752
|
// config/validateEnv.ts
|
|
744
753
|
function validateEthoraConfig() {
|
|
745
754
|
const required = [
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
755
|
+
'ETHORA_CHAT_API_URL',
|
|
756
|
+
'ETHORA_CHAT_APP_ID',
|
|
757
|
+
'ETHORA_CHAT_APP_SECRET',
|
|
749
758
|
];
|
|
750
759
|
|
|
751
760
|
const missing = required.filter((key) => !process.env[key]);
|
|
752
761
|
|
|
753
762
|
if (missing.length > 0) {
|
|
754
763
|
throw new Error(
|
|
755
|
-
`Missing required Ethora environment variables: ${missing.join(
|
|
764
|
+
`Missing required Ethora environment variables: ${missing.join(', ')}`,
|
|
756
765
|
);
|
|
757
766
|
}
|
|
758
767
|
}
|
|
@@ -766,8 +775,8 @@ validateEthoraConfig();
|
|
|
766
775
|
Integrate with your existing logging system:
|
|
767
776
|
|
|
768
777
|
```typescript
|
|
769
|
-
import { getEthoraSDKService } from
|
|
770
|
-
import { logger } from
|
|
778
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
779
|
+
import { logger } from './utils/logger'; // Your logger
|
|
771
780
|
|
|
772
781
|
const chatService = getEthoraSDKService();
|
|
773
782
|
|
|
@@ -789,7 +798,7 @@ async function createChatRoomWithLogging(workspaceId: string) {
|
|
|
789
798
|
Use TypeScript types from the SDK:
|
|
790
799
|
|
|
791
800
|
```typescript
|
|
792
|
-
import type { UUID, ApiResponse } from
|
|
801
|
+
import type { UUID, ApiResponse } from '@ethora/sdk-backend';
|
|
793
802
|
|
|
794
803
|
async function createUserTyped(
|
|
795
804
|
userId: UUID,
|
|
@@ -797,7 +806,7 @@ async function createUserTyped(
|
|
|
797
806
|
firstName: string;
|
|
798
807
|
lastName: string;
|
|
799
808
|
email: string;
|
|
800
|
-
}
|
|
809
|
+
},
|
|
801
810
|
): Promise<ApiResponse> {
|
|
802
811
|
const chatService = getEthoraSDKService();
|
|
803
812
|
return await chatService.createUser(userId, userData);
|
|
@@ -830,7 +839,7 @@ try {
|
|
|
830
839
|
} catch (error) {
|
|
831
840
|
if (axios.isAxiosError(error) && error.response?.status === 422) {
|
|
832
841
|
// User already exists, continue
|
|
833
|
-
console.log(
|
|
842
|
+
console.log('User already exists');
|
|
834
843
|
} else {
|
|
835
844
|
throw error;
|
|
836
845
|
}
|
|
@@ -878,3 +887,6 @@ Apache 2.0
|
|
|
878
887
|
## Support
|
|
879
888
|
|
|
880
889
|
For issues and questions, please open an issue on the GitHub repository.
|
|
890
|
+
|
|
891
|
+
To run tests with logs run from root:
|
|
892
|
+
TEST_LOG_FILE=logs/chat-repo.log npm test
|