@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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @format
|
|
9
9
|
*/
|
|
10
|
-
import type { UUID, ChatRepository, ApiResponse, UpdateUserData, GetUsersQueryParams } from
|
|
10
|
+
import type { UUID, ChatRepository, ApiResponse, UpdateUserData, GetUsersQueryParams } from '../types';
|
|
11
11
|
/**
|
|
12
12
|
* EthoraSDKService - Concrete implementation of ChatRepository
|
|
13
13
|
*/
|
|
@@ -17,16 +17,16 @@ export declare class EthoraSDKService implements ChatRepository {
|
|
|
17
17
|
private readonly httpClient;
|
|
18
18
|
constructor();
|
|
19
19
|
/**
|
|
20
|
-
* Generates a fully-qualified chat room JID from a
|
|
20
|
+
* Generates a fully-qualified chat room JID from a chat ID
|
|
21
21
|
*
|
|
22
|
-
* The JID is constructed in the format `<appId>_<
|
|
22
|
+
* The JID is constructed in the format `<appId>_<chat_id>@conference.xmpp.ethoradev.com`.
|
|
23
23
|
* This method uses a static JID domain to provide a unique identifier for a chat room.
|
|
24
24
|
*
|
|
25
|
-
* @param
|
|
25
|
+
* @param chatId - The unique identifier of the chat
|
|
26
26
|
* @param full - Whether to include the full JID domain
|
|
27
27
|
* @returns The fully-qualified JID string for the chat room
|
|
28
28
|
*/
|
|
29
|
-
createChatName(
|
|
29
|
+
createChatName(chatId: UUID, full?: boolean): string;
|
|
30
30
|
/**
|
|
31
31
|
* Creates a client-side JWT token for a specific user ID
|
|
32
32
|
*
|
|
@@ -64,30 +64,36 @@ export declare class EthoraSDKService implements ChatRepository {
|
|
|
64
64
|
*/
|
|
65
65
|
createUser(userId: UUID, userData?: Record<string, unknown>): Promise<ApiResponse>;
|
|
66
66
|
/**
|
|
67
|
-
* Creates a chat room
|
|
67
|
+
* Creates a chat room
|
|
68
68
|
*
|
|
69
|
-
* @param
|
|
69
|
+
* @param chatId - The unique identifier of the chat
|
|
70
70
|
* @param roomData - Additional room data (optional)
|
|
71
71
|
* @returns The API response
|
|
72
72
|
*/
|
|
73
|
-
createChatRoom(
|
|
73
|
+
createChatRoom(chatId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>;
|
|
74
74
|
/**
|
|
75
75
|
* Grants a user access to a chat room
|
|
76
76
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @param workspaceId - The unique identifier of the workspace
|
|
77
|
+
* @param chatId - The unique identifier of the chat
|
|
80
78
|
* @param userId - The unique identifier of the user (or array of user IDs)
|
|
81
79
|
* @returns The API response
|
|
82
80
|
*/
|
|
83
|
-
grantUserAccessToChatRoom(
|
|
81
|
+
grantUserAccessToChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>;
|
|
84
82
|
/**
|
|
85
83
|
* Grants chatbot access to a chat room
|
|
86
84
|
*
|
|
87
|
-
* @param
|
|
85
|
+
* @param chatId - The unique identifier of the chat
|
|
86
|
+
* @returns The API response
|
|
87
|
+
*/
|
|
88
|
+
grantChatbotAccessToChatRoom(chatId: UUID): Promise<ApiResponse>;
|
|
89
|
+
/**
|
|
90
|
+
* Removes a user's access to a chat room
|
|
91
|
+
*
|
|
92
|
+
* @param chatId - The unique identifier of the chat
|
|
93
|
+
* @param userId - The unique identifier of the user (or array of user IDs)
|
|
88
94
|
* @returns The API response
|
|
89
95
|
*/
|
|
90
|
-
|
|
96
|
+
removeUserAccessFromChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>;
|
|
91
97
|
/**
|
|
92
98
|
* Deletes users from the chat service
|
|
93
99
|
*
|
|
@@ -96,16 +102,16 @@ export declare class EthoraSDKService implements ChatRepository {
|
|
|
96
102
|
*/
|
|
97
103
|
deleteUsers(userIds: UUID[]): Promise<ApiResponse>;
|
|
98
104
|
/**
|
|
99
|
-
* Deletes a chat room from the chat service by its
|
|
105
|
+
* Deletes a chat room from the chat service by its chat ID
|
|
100
106
|
*
|
|
101
|
-
* This method sends a DELETE request to the Ethora API using the
|
|
107
|
+
* This method sends a DELETE request to the Ethora API using the chat ID
|
|
102
108
|
* to construct the chat name. It gracefully handles the case where the chat room
|
|
103
109
|
* is already non-existent (422 Not Found).
|
|
104
110
|
*
|
|
105
|
-
* @param
|
|
111
|
+
* @param chatId - The unique identifier of the chat associated with the chat room
|
|
106
112
|
* @returns The JSON response from the chat service upon successful deletion or a success status if not found
|
|
107
113
|
*/
|
|
108
|
-
deleteChatRoom(
|
|
114
|
+
deleteChatRoom(chatId: UUID): Promise<ApiResponse>;
|
|
109
115
|
/**
|
|
110
116
|
* Updates multiple users in the chat service
|
|
111
117
|
*
|
|
@@ -119,6 +125,9 @@ export declare class EthoraSDKService implements ChatRepository {
|
|
|
119
125
|
* - not-found: user was not found
|
|
120
126
|
* - skipped: user update was skipped
|
|
121
127
|
*
|
|
128
|
+
* Note: The API only accepts xmppUsername, firstName, lastName, username, and profileImage fields.
|
|
129
|
+
* Other fields will be filtered out automatically.
|
|
130
|
+
*
|
|
122
131
|
* @param users - Array of user data to update (1-100 users)
|
|
123
132
|
* @returns The API response with results array containing status for each user
|
|
124
133
|
*/
|
|
@@ -133,6 +142,9 @@ export declare class EthoraSDKService implements ChatRepository {
|
|
|
133
142
|
* - For 1-on-1 chats: use xmppUsernameA-xmppUsernameB format
|
|
134
143
|
* - xmppUsername parameter: returns a specific user by XMPP username
|
|
135
144
|
*
|
|
145
|
+
* Note: The xmppUsername query parameter may not be supported by the API yet.
|
|
146
|
+
* In that case, you can get all users and filter client-side.
|
|
147
|
+
*
|
|
136
148
|
* @param params - Query parameters for filtering users (optional)
|
|
137
149
|
* @returns The API response
|
|
138
150
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EthoraSDKService.d.ts","sourceRoot":"","sources":["../../src/repositories/EthoraSDKService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,cAAc,EACd,WAAW,EAIX,cAAc,EAEd,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAWlB;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;;IAgB3C;;;;;;;;;OASG;IACH,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"EthoraSDKService.d.ts","sourceRoot":"","sources":["../../src/repositories/EthoraSDKService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,IAAI,EACJ,cAAc,EACd,WAAW,EAIX,cAAc,EAEd,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAWlB;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;;IAgB3C;;;;;;;;;OASG;IACH,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM;IAW1D;;;;;;;;;OASG;IACH,sBAAsB,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IAK5C;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;;OAKG;YACW,WAAW;IA4BzB;;;;;;;;;;OAUG;IACG,UAAU,CACd,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IA0EvB;;;;;;OAMG;IACG,cAAc,CAClB,MAAM,EAAE,IAAI,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAsBvB;;;;;;OAMG;IACG,yBAAyB,CAC7B,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GACpB,OAAO,CAAC,WAAW,CAAC;IAsDvB;;;;;OAKG;IACG,4BAA4B,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBtE;;;;;;OAMG;IACG,4BAA4B,CAChC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GACpB,OAAO,CAAC,WAAW,CAAC;IAoDvB;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCxD;;;;;;;;;OASG;IACG,cAAc,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IA8CxD;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA2ChE;;;;;;;;;;;;;;;OAeG;IACG,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;CA6BnE;AAaD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAMtD"}
|
|
@@ -19,7 +19,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
19
19
|
const secrets_1 = require("../config/secrets");
|
|
20
20
|
const jwt_1 = require("../utils/jwt");
|
|
21
21
|
const logger_1 = require("../utils/logger");
|
|
22
|
-
const logger = (0, logger_1.getLogger)(
|
|
22
|
+
const logger = (0, logger_1.getLogger)('EthoraSDKService');
|
|
23
23
|
/**
|
|
24
24
|
* EthoraSDKService - Concrete implementation of ChatRepository
|
|
25
25
|
*/
|
|
@@ -31,26 +31,26 @@ class EthoraSDKService {
|
|
|
31
31
|
this.httpClient = axios_1.default.create({
|
|
32
32
|
timeout: secrets_1.DEFAULT_TIMEOUT.total,
|
|
33
33
|
headers: {
|
|
34
|
-
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
|
-
logger.debug(
|
|
37
|
+
logger.debug('EthoraSDKService instance initialized');
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Generates a fully-qualified chat room JID from a
|
|
40
|
+
* Generates a fully-qualified chat room JID from a chat ID
|
|
41
41
|
*
|
|
42
|
-
* The JID is constructed in the format `<appId>_<
|
|
42
|
+
* The JID is constructed in the format `<appId>_<chat_id>@conference.xmpp.ethoradev.com`.
|
|
43
43
|
* This method uses a static JID domain to provide a unique identifier for a chat room.
|
|
44
44
|
*
|
|
45
|
-
* @param
|
|
45
|
+
* @param chatId - The unique identifier of the chat
|
|
46
46
|
* @param full - Whether to include the full JID domain
|
|
47
47
|
* @returns The fully-qualified JID string for the chat room
|
|
48
48
|
*/
|
|
49
|
-
createChatName(
|
|
50
|
-
logger.debug(`Creating chat room name (JID) for
|
|
49
|
+
createChatName(chatId, full = true) {
|
|
50
|
+
logger.debug(`Creating chat room name (JID) for chat ID: ${chatId}`);
|
|
51
51
|
const chatName = full
|
|
52
|
-
? `${this.secrets.chatAppId}_${
|
|
53
|
-
: `${this.secrets.chatAppId}_${
|
|
52
|
+
? `${this.secrets.chatAppId}_${chatId}${secrets_1.ETHORA_JID_DOMAIN}`
|
|
53
|
+
: `${this.secrets.chatAppId}_${chatId}`;
|
|
54
54
|
logger.info(`Chat room name created: '${chatName}'`);
|
|
55
55
|
return chatName;
|
|
56
56
|
}
|
|
@@ -74,9 +74,9 @@ class EthoraSDKService {
|
|
|
74
74
|
* @returns The headers dictionary containing the `x-custom-token` field
|
|
75
75
|
*/
|
|
76
76
|
getHeaders() {
|
|
77
|
-
logger.debug(
|
|
77
|
+
logger.debug('Retrieving headers for a server-to-server API call');
|
|
78
78
|
return {
|
|
79
|
-
|
|
79
|
+
'x-custom-token': (0, jwt_1.createServerToken)(),
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
@@ -104,7 +104,7 @@ class EthoraSDKService {
|
|
|
104
104
|
`Response: ${axiosError.response?.data}`, error);
|
|
105
105
|
throw error;
|
|
106
106
|
}
|
|
107
|
-
logger.error(
|
|
107
|
+
logger.error('An unexpected error occurred during API call', error);
|
|
108
108
|
throw error;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -121,7 +121,7 @@ class EthoraSDKService {
|
|
|
121
121
|
*/
|
|
122
122
|
async createUser(userId, userData) {
|
|
123
123
|
logger.info(`Attempting to create user with ID: ${userId}`);
|
|
124
|
-
const createUrl = `${this.baseEthoraUrl}/
|
|
124
|
+
const createUrl = `${this.baseEthoraUrl}/v2/users/batch`;
|
|
125
125
|
// Extract user fields from userData or use defaults
|
|
126
126
|
// Generate unique email using UUID if not provided
|
|
127
127
|
const email = userData?.email || `${(0, crypto_1.randomUUID)()}@example.com`;
|
|
@@ -132,22 +132,22 @@ class EthoraSDKService {
|
|
|
132
132
|
let lastName;
|
|
133
133
|
if (userData?.firstName) {
|
|
134
134
|
firstName = userData.firstName;
|
|
135
|
-
lastName = userData?.lastName ||
|
|
135
|
+
lastName = userData?.lastName || '';
|
|
136
136
|
}
|
|
137
137
|
else if (userData?.displayName) {
|
|
138
138
|
const displayName = userData.displayName;
|
|
139
139
|
const nameParts = displayName.trim().split(/\s+/);
|
|
140
|
-
firstName = nameParts[0] ||
|
|
141
|
-
lastName = nameParts.slice(1).join(
|
|
140
|
+
firstName = nameParts[0] || 'User';
|
|
141
|
+
lastName = nameParts.slice(1).join(' ') || '';
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
144
|
-
firstName =
|
|
145
|
-
lastName =
|
|
144
|
+
firstName = 'User';
|
|
145
|
+
lastName = '';
|
|
146
146
|
}
|
|
147
147
|
// Ensure lastName meets API requirements: at least 2 characters, not empty
|
|
148
148
|
if (!lastName || lastName.length < 2) {
|
|
149
149
|
// Use a default lastName if empty or too short
|
|
150
|
-
lastName =
|
|
150
|
+
lastName = 'User';
|
|
151
151
|
}
|
|
152
152
|
// Use plain userId without prefixing
|
|
153
153
|
const userIdStr = String(userId);
|
|
@@ -162,13 +162,13 @@ class EthoraSDKService {
|
|
|
162
162
|
password: password,
|
|
163
163
|
...(userData &&
|
|
164
164
|
Object.fromEntries(Object.entries(userData).filter(([key]) => ![
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
'email',
|
|
166
|
+
'firstName',
|
|
167
|
+
'lastName',
|
|
168
|
+
'password',
|
|
169
|
+
'uuid',
|
|
170
|
+
'displayName',
|
|
171
|
+
'role',
|
|
172
172
|
].includes(key)))),
|
|
173
173
|
},
|
|
174
174
|
],
|
|
@@ -176,32 +176,32 @@ class EthoraSDKService {
|
|
|
176
176
|
logger.debug(`Chat service API URL: ${createUrl}`);
|
|
177
177
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
178
178
|
return this.makeRequest({
|
|
179
|
-
method:
|
|
179
|
+
method: 'POST',
|
|
180
180
|
url: createUrl,
|
|
181
181
|
data: payload,
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
|
-
* Creates a chat room
|
|
185
|
+
* Creates a chat room
|
|
186
186
|
*
|
|
187
|
-
* @param
|
|
187
|
+
* @param chatId - The unique identifier of the chat
|
|
188
188
|
* @param roomData - Additional room data (optional)
|
|
189
189
|
* @returns The API response
|
|
190
190
|
*/
|
|
191
|
-
async createChatRoom(
|
|
192
|
-
logger.info(`Attempting to create chat room
|
|
193
|
-
const createUrl = `${this.baseEthoraUrl}/
|
|
191
|
+
async createChatRoom(chatId, roomData) {
|
|
192
|
+
logger.info(`Attempting to create chat room with ID: ${chatId}`);
|
|
193
|
+
const createUrl = `${this.baseEthoraUrl}/v2/chats`;
|
|
194
194
|
// Create chat room - API expects title, uuid, and type
|
|
195
195
|
const payload = {
|
|
196
|
-
title: roomData?.title || `Chat Room ${
|
|
197
|
-
uuid: String(
|
|
198
|
-
type: roomData?.type ||
|
|
196
|
+
title: roomData?.title || `Chat Room ${chatId}`,
|
|
197
|
+
uuid: String(chatId), // Use chatId as uuid
|
|
198
|
+
type: roomData?.type || 'group',
|
|
199
199
|
...roomData, // Allow roomData to override fields if provided
|
|
200
200
|
};
|
|
201
201
|
logger.debug(`Chat service API URL: ${createUrl}`);
|
|
202
202
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
203
203
|
return this.makeRequest({
|
|
204
|
-
method:
|
|
204
|
+
method: 'POST',
|
|
205
205
|
url: createUrl,
|
|
206
206
|
data: payload,
|
|
207
207
|
});
|
|
@@ -209,32 +209,42 @@ class EthoraSDKService {
|
|
|
209
209
|
/**
|
|
210
210
|
* Grants a user access to a chat room
|
|
211
211
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* @param workspaceId - The unique identifier of the workspace
|
|
212
|
+
* @param chatId - The unique identifier of the chat
|
|
215
213
|
* @param userId - The unique identifier of the user (or array of user IDs)
|
|
216
214
|
* @returns The API response
|
|
217
215
|
*/
|
|
218
|
-
async grantUserAccessToChatRoom(
|
|
219
|
-
logger.info(`Granting user(s) access to chat room
|
|
220
|
-
const chatName = this.createChatName(
|
|
221
|
-
// Use /
|
|
222
|
-
const grantUrl = `${this.baseEthoraUrl}/
|
|
216
|
+
async grantUserAccessToChatRoom(chatId, userId) {
|
|
217
|
+
logger.info(`Granting user(s) access to chat room ${chatId}`);
|
|
218
|
+
const chatName = this.createChatName(chatId, false);
|
|
219
|
+
// Use /v2/chats/users-access endpoint with chatName and members array
|
|
220
|
+
const grantUrl = `${this.baseEthoraUrl}/v2/chats/users-access`;
|
|
223
221
|
// Convert single userId to array if needed
|
|
224
|
-
//
|
|
222
|
+
// API requires usernames to start with appId, so prefix them
|
|
225
223
|
const members = Array.isArray(userId)
|
|
226
|
-
? userId.map((id) =>
|
|
227
|
-
|
|
224
|
+
? userId.map((id) => {
|
|
225
|
+
const userIdStr = String(id);
|
|
226
|
+
// If userId doesn't start with appId, prefix it
|
|
227
|
+
return userIdStr.startsWith(this.secrets.chatAppId)
|
|
228
|
+
? userIdStr
|
|
229
|
+
: `${this.secrets.chatAppId}_${userIdStr}`;
|
|
230
|
+
})
|
|
231
|
+
: [
|
|
232
|
+
(() => {
|
|
233
|
+
const userIdStr = String(userId);
|
|
234
|
+
return userIdStr.startsWith(this.secrets.chatAppId)
|
|
235
|
+
? userIdStr
|
|
236
|
+
: `${this.secrets.chatAppId}_${userIdStr}`;
|
|
237
|
+
})(),
|
|
238
|
+
];
|
|
228
239
|
const payload = {
|
|
229
240
|
chatName: chatName,
|
|
230
241
|
members: members,
|
|
231
242
|
};
|
|
232
243
|
logger.debug(`Chat service API URL: ${grantUrl}`);
|
|
233
244
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
234
|
-
logger.debug(`Using XMPP usernames for members: ${members.join(", ")}`);
|
|
235
245
|
try {
|
|
236
246
|
return await this.makeRequest({
|
|
237
|
-
method:
|
|
247
|
+
method: 'POST',
|
|
238
248
|
url: grantUrl,
|
|
239
249
|
data: payload,
|
|
240
250
|
});
|
|
@@ -243,8 +253,6 @@ class EthoraSDKService {
|
|
|
243
253
|
if (axios_1.default.isAxiosError(error)) {
|
|
244
254
|
const errorData = error.response?.data;
|
|
245
255
|
logger.error(`Failed to grant user access. Status: ${error.response?.status}, Response: ${JSON.stringify(errorData)}`, error);
|
|
246
|
-
// Log the XMPP usernames that were attempted for debugging
|
|
247
|
-
logger.debug(`Attempted XMPP usernames: ${members.join(", ")}`);
|
|
248
256
|
}
|
|
249
257
|
throw error;
|
|
250
258
|
}
|
|
@@ -252,20 +260,69 @@ class EthoraSDKService {
|
|
|
252
260
|
/**
|
|
253
261
|
* Grants chatbot access to a chat room
|
|
254
262
|
*
|
|
255
|
-
* @param
|
|
263
|
+
* @param chatId - The unique identifier of the chat
|
|
256
264
|
* @returns The API response
|
|
257
265
|
*/
|
|
258
|
-
async grantChatbotAccessToChatRoom(
|
|
259
|
-
logger.info(`Granting chatbot access to chat room
|
|
266
|
+
async grantChatbotAccessToChatRoom(chatId) {
|
|
267
|
+
logger.info(`Granting chatbot access to chat room ${chatId}`);
|
|
260
268
|
if (!this.secrets.chatBotJid) {
|
|
261
|
-
const error = new Error(
|
|
262
|
-
logger.error(
|
|
269
|
+
const error = new Error('Chatbot JID not configured. Set ETHORA_CHAT_BOT_JID environment variable.');
|
|
270
|
+
logger.error('Cannot grant chatbot access', error);
|
|
263
271
|
throw error;
|
|
264
272
|
}
|
|
265
273
|
// Extract username from JID (format: "username@domain" -> "username")
|
|
266
|
-
const chatbotUsername = this.secrets.chatBotJid.split(
|
|
274
|
+
const chatbotUsername = this.secrets.chatBotJid.split('@')[0];
|
|
267
275
|
// Use the same grant access method with chatbot JID
|
|
268
|
-
return this.grantUserAccessToChatRoom(
|
|
276
|
+
return this.grantUserAccessToChatRoom(chatId, chatbotUsername);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Removes a user's access to a chat room
|
|
280
|
+
*
|
|
281
|
+
* @param chatId - The unique identifier of the chat
|
|
282
|
+
* @param userId - The unique identifier of the user (or array of user IDs)
|
|
283
|
+
* @returns The API response
|
|
284
|
+
*/
|
|
285
|
+
async removeUserAccessFromChatRoom(chatId, userId) {
|
|
286
|
+
logger.info(`Removing user(s) access from chat room ${chatId}`);
|
|
287
|
+
const chatName = this.createChatName(chatId, false);
|
|
288
|
+
// Use /v2/chats/usersAccess/remove DELETE endpoint (fallback to legacy /v2/chats/users-access)
|
|
289
|
+
const revokeUrl = `${this.baseEthoraUrl}/v2/chats/users-access`;
|
|
290
|
+
// Convert single userId to array if needed
|
|
291
|
+
const members = Array.isArray(userId)
|
|
292
|
+
? userId.map((id) => {
|
|
293
|
+
const userIdStr = String(id);
|
|
294
|
+
return userIdStr.startsWith(this.secrets.chatAppId)
|
|
295
|
+
? userIdStr
|
|
296
|
+
: `${this.secrets.chatAppId}_${userIdStr}`;
|
|
297
|
+
})
|
|
298
|
+
: [
|
|
299
|
+
(() => {
|
|
300
|
+
const userIdStr = String(userId);
|
|
301
|
+
return userIdStr.startsWith(this.secrets.chatAppId)
|
|
302
|
+
? userIdStr
|
|
303
|
+
: `${this.secrets.chatAppId}_${userIdStr}`;
|
|
304
|
+
})(),
|
|
305
|
+
];
|
|
306
|
+
const payload = {
|
|
307
|
+
chatName: chatName,
|
|
308
|
+
members: members,
|
|
309
|
+
};
|
|
310
|
+
logger.debug(`Chat service API URL: ${revokeUrl}`);
|
|
311
|
+
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
312
|
+
try {
|
|
313
|
+
return await this.makeRequest({
|
|
314
|
+
method: 'DELETE',
|
|
315
|
+
url: revokeUrl,
|
|
316
|
+
data: payload,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
321
|
+
const errorData = error.response?.data;
|
|
322
|
+
logger.error(`Failed to remove user access. Status: ${error.response?.status}, Response: ${JSON.stringify(errorData)}`, error);
|
|
323
|
+
}
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
269
326
|
}
|
|
270
327
|
/**
|
|
271
328
|
* Deletes users from the chat service
|
|
@@ -274,7 +331,7 @@ class EthoraSDKService {
|
|
|
274
331
|
* @returns The API response
|
|
275
332
|
*/
|
|
276
333
|
async deleteUsers(userIds) {
|
|
277
|
-
logger.info(`Attempting to delete users: ${userIds.join(
|
|
334
|
+
logger.info(`Attempting to delete users: ${userIds.join(', ')}`);
|
|
278
335
|
const deleteUrl = `${this.baseEthoraUrl}/v1/users/batch`;
|
|
279
336
|
const payload = {
|
|
280
337
|
usersIdList: userIds.map((id) => String(id)),
|
|
@@ -283,7 +340,7 @@ class EthoraSDKService {
|
|
|
283
340
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
284
341
|
try {
|
|
285
342
|
return await this.makeRequest({
|
|
286
|
-
method:
|
|
343
|
+
method: 'DELETE',
|
|
287
344
|
url: deleteUrl,
|
|
288
345
|
data: payload,
|
|
289
346
|
});
|
|
@@ -292,29 +349,29 @@ class EthoraSDKService {
|
|
|
292
349
|
// Handle the case where users don't exist (422 with "not found")
|
|
293
350
|
if (axios_1.default.isAxiosError(error) &&
|
|
294
351
|
error.response?.status === 422 &&
|
|
295
|
-
typeof error.response.data ===
|
|
296
|
-
error.response.data.includes(
|
|
297
|
-
logger.info(
|
|
352
|
+
typeof error.response.data === 'string' &&
|
|
353
|
+
error.response.data.includes('not found')) {
|
|
354
|
+
logger.info('No users to delete from the chat service. The request contained non-existent users.');
|
|
298
355
|
return { ok: false };
|
|
299
356
|
}
|
|
300
357
|
throw error;
|
|
301
358
|
}
|
|
302
359
|
}
|
|
303
360
|
/**
|
|
304
|
-
* Deletes a chat room from the chat service by its
|
|
361
|
+
* Deletes a chat room from the chat service by its chat ID
|
|
305
362
|
*
|
|
306
|
-
* This method sends a DELETE request to the Ethora API using the
|
|
363
|
+
* This method sends a DELETE request to the Ethora API using the chat ID
|
|
307
364
|
* to construct the chat name. It gracefully handles the case where the chat room
|
|
308
365
|
* is already non-existent (422 Not Found).
|
|
309
366
|
*
|
|
310
|
-
* @param
|
|
367
|
+
* @param chatId - The unique identifier of the chat associated with the chat room
|
|
311
368
|
* @returns The JSON response from the chat service upon successful deletion or a success status if not found
|
|
312
369
|
*/
|
|
313
|
-
async deleteChatRoom(
|
|
314
|
-
logger.info(`Attempting to delete chat room
|
|
370
|
+
async deleteChatRoom(chatId) {
|
|
371
|
+
logger.info(`Attempting to delete chat room with ID: ${chatId}`);
|
|
315
372
|
const deleteUrl = `${this.baseEthoraUrl}/v1/chats`;
|
|
316
373
|
// We must use the short name when deleting the chat room
|
|
317
|
-
const chatName = this.createChatName(
|
|
374
|
+
const chatName = this.createChatName(chatId, false);
|
|
318
375
|
const payload = {
|
|
319
376
|
name: chatName,
|
|
320
377
|
};
|
|
@@ -322,7 +379,7 @@ class EthoraSDKService {
|
|
|
322
379
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
323
380
|
try {
|
|
324
381
|
const response = await this.makeRequest({
|
|
325
|
-
method:
|
|
382
|
+
method: 'DELETE',
|
|
326
383
|
url: deleteUrl,
|
|
327
384
|
data: payload,
|
|
328
385
|
});
|
|
@@ -336,10 +393,10 @@ class EthoraSDKService {
|
|
|
336
393
|
const responseText = axiosError.response?.data;
|
|
337
394
|
// Handle the case where the room does not exist (Ethora returns 422 with "not found" in body)
|
|
338
395
|
if (statusCode === 422 &&
|
|
339
|
-
typeof responseText ===
|
|
340
|
-
responseText.toLowerCase().includes(
|
|
396
|
+
typeof responseText === 'string' &&
|
|
397
|
+
responseText.toLowerCase().includes('not found')) {
|
|
341
398
|
logger.warn(`Chat room '${chatName}' not found during deletion attempt (Ignored 422)`);
|
|
342
|
-
return { ok: false, reason:
|
|
399
|
+
return { ok: false, reason: 'Chat room not found' };
|
|
343
400
|
}
|
|
344
401
|
}
|
|
345
402
|
// Re-throw if it's not a "not found" case
|
|
@@ -359,24 +416,39 @@ class EthoraSDKService {
|
|
|
359
416
|
* - not-found: user was not found
|
|
360
417
|
* - skipped: user update was skipped
|
|
361
418
|
*
|
|
419
|
+
* Note: The API only accepts xmppUsername, firstName, lastName, username, and profileImage fields.
|
|
420
|
+
* Other fields will be filtered out automatically.
|
|
421
|
+
*
|
|
362
422
|
* @param users - Array of user data to update (1-100 users)
|
|
363
423
|
* @returns The API response with results array containing status for each user
|
|
364
424
|
*/
|
|
365
425
|
async updateUsers(users) {
|
|
366
426
|
// Validate user count limit
|
|
367
427
|
if (users.length === 0) {
|
|
368
|
-
throw new Error(
|
|
428
|
+
throw new Error('At least 1 user is required for update');
|
|
369
429
|
}
|
|
370
430
|
if (users.length > 100) {
|
|
371
|
-
throw new Error(
|
|
431
|
+
throw new Error('Maximum 100 users allowed per update request');
|
|
372
432
|
}
|
|
373
433
|
logger.info(`Attempting to update ${users.length} user(s)`);
|
|
374
|
-
const updateUrl = `${this.baseEthoraUrl}/
|
|
434
|
+
const updateUrl = `${this.baseEthoraUrl}/v2/chats/users`;
|
|
375
435
|
// Remove userId from payload if present, as API doesn't accept it
|
|
376
436
|
// API expects xmppUsername or other identifier fields instead
|
|
437
|
+
// Also filter to only include allowed fields: xmppUsername, firstName, lastName, username, profileImage
|
|
377
438
|
const cleanedUsers = users.map((user) => {
|
|
378
439
|
const { userId, ...rest } = user;
|
|
379
|
-
|
|
440
|
+
const allowedFields = {};
|
|
441
|
+
if (rest.xmppUsername)
|
|
442
|
+
allowedFields.xmppUsername = rest.xmppUsername;
|
|
443
|
+
if (rest.firstName)
|
|
444
|
+
allowedFields.firstName = rest.firstName;
|
|
445
|
+
if (rest.lastName)
|
|
446
|
+
allowedFields.lastName = rest.lastName;
|
|
447
|
+
if (rest.username)
|
|
448
|
+
allowedFields.username = rest.username;
|
|
449
|
+
if (rest.profileImage)
|
|
450
|
+
allowedFields.profileImage = rest.profileImage;
|
|
451
|
+
return allowedFields;
|
|
380
452
|
});
|
|
381
453
|
const payload = {
|
|
382
454
|
users: cleanedUsers,
|
|
@@ -384,7 +456,7 @@ class EthoraSDKService {
|
|
|
384
456
|
logger.debug(`Chat service API URL: ${updateUrl}`);
|
|
385
457
|
logger.debug(`Request payload: ${JSON.stringify(payload)}`);
|
|
386
458
|
return this.makeRequest({
|
|
387
|
-
method:
|
|
459
|
+
method: 'PATCH',
|
|
388
460
|
url: updateUrl,
|
|
389
461
|
data: payload,
|
|
390
462
|
});
|
|
@@ -399,11 +471,14 @@ class EthoraSDKService {
|
|
|
399
471
|
* - For 1-on-1 chats: use xmppUsernameA-xmppUsernameB format
|
|
400
472
|
* - xmppUsername parameter: returns a specific user by XMPP username
|
|
401
473
|
*
|
|
474
|
+
* Note: The xmppUsername query parameter may not be supported by the API yet.
|
|
475
|
+
* In that case, you can get all users and filter client-side.
|
|
476
|
+
*
|
|
402
477
|
* @param params - Query parameters for filtering users (optional)
|
|
403
478
|
* @returns The API response
|
|
404
479
|
*/
|
|
405
480
|
async getUsers(params) {
|
|
406
|
-
const getUrl = `${this.baseEthoraUrl}/
|
|
481
|
+
const getUrl = `${this.baseEthoraUrl}/v2/chats/users`;
|
|
407
482
|
// Build query parameters
|
|
408
483
|
const queryParams = [];
|
|
409
484
|
if (params?.chatName) {
|
|
@@ -412,13 +487,13 @@ class EthoraSDKService {
|
|
|
412
487
|
if (params?.xmppUsername) {
|
|
413
488
|
queryParams.push(`xmppUsername=${encodeURIComponent(params.xmppUsername)}`);
|
|
414
489
|
}
|
|
415
|
-
const urlWithParams = queryParams.length > 0 ? `${getUrl}?${queryParams.join(
|
|
490
|
+
const urlWithParams = queryParams.length > 0 ? `${getUrl}?${queryParams.join('&')}` : getUrl;
|
|
416
491
|
logger.info(params
|
|
417
492
|
? `Getting users with params: ${JSON.stringify(params)}`
|
|
418
|
-
:
|
|
493
|
+
: 'Getting all users of the app');
|
|
419
494
|
logger.debug(`Chat service API URL: ${urlWithParams}`);
|
|
420
495
|
return this.makeRequest({
|
|
421
|
-
method:
|
|
496
|
+
method: 'GET',
|
|
422
497
|
url: urlWithParams,
|
|
423
498
|
});
|
|
424
499
|
}
|
|
@@ -436,7 +511,7 @@ exports.EthoraSDKService = EthoraSDKService;
|
|
|
436
511
|
let repositoryInstance = null;
|
|
437
512
|
function getEthoraSDKService() {
|
|
438
513
|
if (!repositoryInstance) {
|
|
439
|
-
logger.debug(
|
|
514
|
+
logger.debug('Creating new EthoraSDKService instance');
|
|
440
515
|
repositoryInstance = new EthoraSDKService();
|
|
441
516
|
}
|
|
442
517
|
return repositoryInstance;
|