@go-avro/avro-js 0.0.2-beta.1 → 0.0.2-beta.100
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 +1 -0
- package/dist/auth/AuthManager.d.ts +13 -3
- package/dist/auth/AuthManager.js +102 -28
- package/dist/auth/storage.d.ts +8 -8
- package/dist/auth/storage.js +12 -10
- package/dist/client/QueryClient.d.ts +372 -7
- package/dist/client/QueryClient.js +381 -96
- package/dist/client/core/fetch.d.ts +1 -0
- package/dist/client/core/fetch.js +62 -0
- package/dist/client/core/utils.d.ts +1 -0
- package/dist/client/core/utils.js +14 -0
- package/dist/client/core/xhr.d.ts +1 -0
- package/dist/client/core/xhr.js +84 -0
- package/dist/client/hooks/analytics.d.ts +1 -0
- package/dist/client/hooks/analytics.js +10 -0
- package/dist/client/hooks/avro.d.ts +1 -0
- package/dist/client/hooks/avro.js +9 -0
- package/dist/client/hooks/bills.d.ts +1 -0
- package/dist/client/hooks/bills.js +141 -0
- package/dist/client/hooks/chats.d.ts +1 -0
- package/dist/client/hooks/chats.js +37 -0
- package/dist/client/hooks/companies.d.ts +1 -0
- package/dist/client/hooks/companies.js +90 -0
- package/dist/client/hooks/events.d.ts +1 -0
- package/dist/client/hooks/events.js +307 -0
- package/dist/client/hooks/jobs.d.ts +1 -0
- package/dist/client/hooks/jobs.js +178 -0
- package/dist/client/hooks/messages.d.ts +1 -0
- package/dist/client/hooks/messages.js +30 -0
- package/dist/client/hooks/months.d.ts +1 -0
- package/dist/client/hooks/months.js +92 -0
- package/dist/client/hooks/plans.d.ts +1 -0
- package/dist/client/hooks/plans.js +9 -0
- package/dist/client/hooks/root.d.ts +1 -0
- package/dist/client/hooks/root.js +8 -0
- package/dist/client/hooks/routes.d.ts +1 -0
- package/dist/client/hooks/routes.js +168 -0
- package/dist/client/hooks/sessions.d.ts +1 -0
- package/dist/client/hooks/sessions.js +175 -0
- package/dist/client/hooks/teams.d.ts +1 -0
- package/dist/client/hooks/teams.js +116 -0
- package/dist/client/hooks/users.d.ts +1 -0
- package/dist/client/hooks/users.js +104 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/types/api.d.ts +588 -9
- package/dist/types/api.js +10 -1
- package/dist/types/auth.d.ts +0 -5
- package/dist/types/cache.d.ts +9 -0
- package/dist/types/cache.js +1 -0
- package/package.json +6 -4
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import io from 'socket.io-client';
|
|
2
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { LoginResponse } from '../types/api';
|
|
1
4
|
import { StandardError } from '../types/error';
|
|
2
5
|
export class AvroQueryClient {
|
|
3
6
|
constructor(config) {
|
|
7
|
+
this._isAuthenticated = false;
|
|
8
|
+
this.companyId = null;
|
|
4
9
|
this.config = {
|
|
5
10
|
baseUrl: config.baseUrl,
|
|
6
11
|
authManager: config.authManager,
|
|
@@ -8,112 +13,392 @@ export class AvroQueryClient {
|
|
|
8
13
|
retryStrategy: config.retryStrategy ?? 'fixed',
|
|
9
14
|
timeout: config.timeout ?? 0,
|
|
10
15
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
config.authManager.isAuthenticated().then(isAuth => {
|
|
17
|
+
this._isAuthenticated = isAuth;
|
|
18
|
+
});
|
|
19
|
+
config.authManager.getCompanyId().then(companyId => {
|
|
20
|
+
this.companyId = companyId;
|
|
21
|
+
});
|
|
22
|
+
this.socket = io(config.baseUrl, { autoConnect: false, transports: ["websocket"], });
|
|
23
|
+
if (!this.socket.connected) {
|
|
24
|
+
this.config.authManager.accessToken().then(token => {
|
|
25
|
+
console.log('Initializing socket connection with token...');
|
|
26
|
+
this.socket.auth = { token: token };
|
|
27
|
+
this.socket.connect();
|
|
28
|
+
});
|
|
18
29
|
}
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
this.socket.on('connect', () => {
|
|
31
|
+
this._isAuthenticated = true;
|
|
32
|
+
console.log(`Socket connected with ID: ${this.socket?.id}`);
|
|
33
|
+
});
|
|
34
|
+
this.socket.on('disconnect', (reason) => {
|
|
35
|
+
console.log(`Socket disconnected: ${reason}`);
|
|
36
|
+
});
|
|
37
|
+
this.socket.on('connect_error', (err) => {
|
|
38
|
+
console.error(`Socket connection error: ${err.message}`);
|
|
39
|
+
});
|
|
40
|
+
this.config.authManager.onTokenRefreshed((newAccessToken) => {
|
|
41
|
+
if (this.socket && newAccessToken) {
|
|
42
|
+
this._isAuthenticated = true;
|
|
43
|
+
console.log('Access token refreshed, updating socket auth...');
|
|
44
|
+
this.socket.auth = { token: newAccessToken };
|
|
45
|
+
this.socket.disconnect().connect();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
config.authManager.onTokenRefreshFailed(() => {
|
|
49
|
+
this._isAuthenticated = false;
|
|
50
|
+
if (this.socket && this.socket.connected) {
|
|
51
|
+
this.socket.disconnect();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
emit(eventName, data) {
|
|
56
|
+
if (!this.socket?.connected) {
|
|
57
|
+
console.error('Socket is not connected. Cannot emit event.');
|
|
58
|
+
return;
|
|
21
59
|
}
|
|
22
|
-
|
|
60
|
+
this.socket.emit(eventName, data);
|
|
23
61
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return this.config.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.config.authManager
|
|
51
|
-
.refreshTokens()
|
|
52
|
-
.then(() => {
|
|
53
|
-
this._xhr(method, path, body, cancelToken, headers, isIdempotent, retryCount + 1).then(resolve, reject);
|
|
54
|
-
})
|
|
55
|
-
.catch(() => {
|
|
56
|
-
reject(new StandardError(401, 'Unauthorized (refresh failed)'));
|
|
57
|
-
});
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
61
|
-
try {
|
|
62
|
-
resolve(JSON.parse(xhr.responseText));
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
resolve(xhr.responseText);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
if (retryCount < this.config.maxRetries) {
|
|
70
|
-
const delay = this.getDelay(this.config.retryStrategy, retryCount);
|
|
71
|
-
setTimeout(() => {
|
|
72
|
-
this._xhr(method, path, body, cancelToken, headers, isIdempotent, retryCount + 1).then(resolve, reject);
|
|
73
|
-
}, delay);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
let msg = xhr.statusText;
|
|
77
|
-
try {
|
|
78
|
-
const parsed = JSON.parse(xhr.responseText);
|
|
79
|
-
msg = parsed.message || msg;
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
console.warn('Failed to parse error response:', xhr.responseText);
|
|
83
|
-
}
|
|
84
|
-
reject(new StandardError(xhr.status, msg));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
xhr.onerror = () => {
|
|
89
|
-
if (retryCount < this.config.maxRetries) {
|
|
90
|
-
const delay = this.getDelay(this.config.retryStrategy, retryCount);
|
|
91
|
-
setTimeout(() => {
|
|
92
|
-
this._xhr(method, path, body, cancelToken, headers, isIdempotent, retryCount + 1).then(resolve, reject);
|
|
93
|
-
}, delay);
|
|
62
|
+
on(eventName, callback) {
|
|
63
|
+
this.socket?.on(eventName, callback);
|
|
64
|
+
}
|
|
65
|
+
off(eventName, callback) {
|
|
66
|
+
this.socket?.off(eventName, callback);
|
|
67
|
+
}
|
|
68
|
+
get(path, cancelToken, headers = {}, progressUpdateCallback) {
|
|
69
|
+
return this._xhr('GET', path, null, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
|
|
70
|
+
}
|
|
71
|
+
post(path, data, cancelToken, headers = {}, progressUpdateCallback) {
|
|
72
|
+
return this._xhr('POST', path, data, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
|
|
73
|
+
}
|
|
74
|
+
put(path, data, cancelToken, headers = {}, progressUpdateCallback) {
|
|
75
|
+
return this._xhr('PUT', path, data, cancelToken, headers, true, this.config.maxRetries, progressUpdateCallback);
|
|
76
|
+
}
|
|
77
|
+
delete(path, cancelToken, headers = {}, progressUpdateCallback) {
|
|
78
|
+
return this._xhr('DELETE', path, null, cancelToken, headers, false, this.config.maxRetries, progressUpdateCallback);
|
|
79
|
+
}
|
|
80
|
+
useLogin() {
|
|
81
|
+
const queryClient = useQueryClient();
|
|
82
|
+
return useMutation({
|
|
83
|
+
mutationFn: async ({ username, password, code, cancelToken }) => {
|
|
84
|
+
const resp = await this.post('/login', JSON.stringify({ username, password, code }), cancelToken, { 'Content-Type': 'application/json' });
|
|
85
|
+
if (!resp || !('access_token' in resp)) {
|
|
86
|
+
if (resp.msg === "TOTP required") {
|
|
87
|
+
return LoginResponse.NEEDS_TOTP;
|
|
94
88
|
}
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
throw new StandardError(401, 'Invalid login response');
|
|
90
|
+
}
|
|
91
|
+
this._isAuthenticated = true;
|
|
92
|
+
this.socket.auth = { token: resp.access_token };
|
|
93
|
+
if (!this.socket.connected) {
|
|
94
|
+
this.socket.connect();
|
|
95
|
+
}
|
|
96
|
+
await this.config.authManager.setTokens({ access_token: resp.access_token, refresh_token: resp.refresh_token });
|
|
97
|
+
return LoginResponse.SUCCESS;
|
|
98
|
+
},
|
|
99
|
+
onSettled: () => {
|
|
100
|
+
queryClient.invalidateQueries();
|
|
101
|
+
},
|
|
102
|
+
onError: (err) => {
|
|
103
|
+
this.config.authManager.clearCache();
|
|
104
|
+
throw new StandardError(401, err.message || 'Login failed');
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
useRequestCode() {
|
|
109
|
+
const queryClient = useQueryClient();
|
|
110
|
+
return useMutation({
|
|
111
|
+
mutationFn: async ({ username, email, cancelToken }) => {
|
|
112
|
+
const resp = await this.post('/code', JSON.stringify({ username, email }), cancelToken, { 'Content-Type': 'application/json' });
|
|
113
|
+
return resp;
|
|
114
|
+
},
|
|
115
|
+
onSettled: () => {
|
|
116
|
+
queryClient.invalidateQueries();
|
|
117
|
+
},
|
|
118
|
+
onError: (err) => {
|
|
119
|
+
throw new StandardError(err.status, err.message || 'Request code failed');
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
useUpdatePassword() {
|
|
124
|
+
const queryClient = useQueryClient();
|
|
125
|
+
return useMutation({
|
|
126
|
+
mutationFn: async ({ username, email, code, newPassword, cancelToken }) => {
|
|
127
|
+
await this.post(`/user/${username ?? email}/password`, JSON.stringify({ code, password: newPassword }), cancelToken, { 'Content-Type': 'application/json' });
|
|
128
|
+
},
|
|
129
|
+
onSettled: () => {
|
|
130
|
+
queryClient.invalidateQueries();
|
|
131
|
+
},
|
|
132
|
+
onError: (err) => {
|
|
133
|
+
throw new StandardError(err.status, err.message || 'Update password failed');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
useGoogleLogin() {
|
|
138
|
+
const queryClient = useQueryClient();
|
|
139
|
+
return useMutation({
|
|
140
|
+
mutationFn: async ({ token, cancelToken }) => {
|
|
141
|
+
const resp = await this._xhr('POST', `/google/authorize?token=${token}`, {}, cancelToken, { 'Content-Type': 'application/json' });
|
|
142
|
+
if (!resp || !('access_token' in resp)) {
|
|
143
|
+
if (resp.msg === "TOTP required") {
|
|
144
|
+
return LoginResponse.NEEDS_TOTP;
|
|
97
145
|
}
|
|
98
|
-
|
|
99
|
-
if (this.config.timeout) {
|
|
100
|
-
xhr.timeout = this.config.timeout;
|
|
101
|
-
xhr.ontimeout = () => reject(new StandardError(0, 'Request timed out'));
|
|
146
|
+
throw new StandardError(401, 'Invalid Google login response');
|
|
102
147
|
}
|
|
103
|
-
|
|
104
|
-
|
|
148
|
+
this.socket.auth = { token: resp.access_token };
|
|
149
|
+
if (!this.socket.connected) {
|
|
150
|
+
this.socket.connect();
|
|
151
|
+
}
|
|
152
|
+
await this.config.authManager.setTokens({ access_token: resp.access_token, refresh_token: resp.refresh_token });
|
|
153
|
+
return LoginResponse.SUCCESS;
|
|
154
|
+
},
|
|
155
|
+
onSettled: () => {
|
|
156
|
+
queryClient.invalidateQueries();
|
|
157
|
+
},
|
|
158
|
+
onError: (err) => {
|
|
159
|
+
this.config.authManager.clearCache();
|
|
160
|
+
throw new StandardError(err.status, err.message || 'Google Login failed');
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
setTokens(tokens) {
|
|
165
|
+
return this.config.authManager.setTokens(tokens);
|
|
166
|
+
}
|
|
167
|
+
setCache(data) {
|
|
168
|
+
return this.config.authManager.setCache(data);
|
|
169
|
+
}
|
|
170
|
+
setCompanyId(companyId) {
|
|
171
|
+
this.companyId = companyId;
|
|
172
|
+
this.config.authManager.setCompanyId(companyId);
|
|
173
|
+
}
|
|
174
|
+
clearCache() {
|
|
175
|
+
return this.config.authManager.clearCache();
|
|
176
|
+
}
|
|
177
|
+
isAuthenticated() {
|
|
178
|
+
return this._isAuthenticated;
|
|
179
|
+
}
|
|
180
|
+
isAuthenticatedAsync() {
|
|
181
|
+
return this.config.authManager.isAuthenticated();
|
|
182
|
+
}
|
|
183
|
+
useLogout() {
|
|
184
|
+
const queryClient = useQueryClient();
|
|
185
|
+
return useMutation({
|
|
186
|
+
mutationFn: async (cancelToken) => {
|
|
187
|
+
await this.post('/logout', null, cancelToken);
|
|
188
|
+
await this.config.authManager.clearCache();
|
|
189
|
+
if (this.socket && this.socket.connected) {
|
|
190
|
+
this.socket.disconnect();
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
onSettled: () => {
|
|
194
|
+
this._isAuthenticated = false;
|
|
195
|
+
queryClient.invalidateQueries();
|
|
196
|
+
},
|
|
197
|
+
onError: (err) => {
|
|
198
|
+
this.config.authManager.clearCache();
|
|
199
|
+
console.error('Logout failed:', err);
|
|
200
|
+
throw new StandardError(500, 'Logout failed');
|
|
201
|
+
}
|
|
105
202
|
});
|
|
106
203
|
}
|
|
107
|
-
|
|
108
|
-
|
|
204
|
+
fetchJobs(body = {}, cancelToken, headers = {}) {
|
|
205
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
206
|
+
throw new StandardError(400, 'Company ID is required');
|
|
207
|
+
}
|
|
208
|
+
return this._fetch('POST', `/company/${this.companyId}/jobs`, JSON.stringify(body), cancelToken, {
|
|
209
|
+
...headers,
|
|
210
|
+
'Content-Type': 'application/json',
|
|
211
|
+
})
|
|
212
|
+
.then(response => {
|
|
213
|
+
if (!response || !Array.isArray(response)) {
|
|
214
|
+
throw new StandardError(400, 'Invalid jobs response');
|
|
215
|
+
}
|
|
216
|
+
return response;
|
|
217
|
+
})
|
|
218
|
+
.catch(err => {
|
|
219
|
+
console.error('Failed to fetch jobs:', err);
|
|
220
|
+
throw new StandardError(500, 'Failed to fetch jobs');
|
|
221
|
+
});
|
|
109
222
|
}
|
|
110
|
-
|
|
111
|
-
|
|
223
|
+
fetchChats(body = {}, cancelToken, headers = {}) {
|
|
224
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
225
|
+
throw new StandardError(400, 'Company ID is required');
|
|
226
|
+
}
|
|
227
|
+
return this._fetch('POST', `/company/${this.companyId}/chats`, JSON.stringify(body), cancelToken, {
|
|
228
|
+
...headers,
|
|
229
|
+
'Content-Type': 'application/json',
|
|
230
|
+
})
|
|
231
|
+
.then(response => {
|
|
232
|
+
if (!response || !Array.isArray(response)) {
|
|
233
|
+
throw new StandardError(400, 'Invalid chats response');
|
|
234
|
+
}
|
|
235
|
+
return response;
|
|
236
|
+
})
|
|
237
|
+
.catch(err => {
|
|
238
|
+
console.error('Failed to fetch chats:', err);
|
|
239
|
+
throw new StandardError(500, 'Failed to fetch chats');
|
|
240
|
+
});
|
|
112
241
|
}
|
|
113
|
-
|
|
114
|
-
|
|
242
|
+
fetchMessages(chatId, body = {}, cancelToken, headers = {}) {
|
|
243
|
+
if (!chatId || chatId.trim() === '') {
|
|
244
|
+
throw new StandardError(400, 'Chat ID is required');
|
|
245
|
+
}
|
|
246
|
+
return this._fetch('POST', `/chat/${chatId}/messages`, JSON.stringify(body), cancelToken, {
|
|
247
|
+
...headers,
|
|
248
|
+
'Content-Type': 'application/json',
|
|
249
|
+
})
|
|
250
|
+
.then(response => {
|
|
251
|
+
if (!response || !Array.isArray(response)) {
|
|
252
|
+
throw new StandardError(400, 'Invalid messages response');
|
|
253
|
+
}
|
|
254
|
+
return response;
|
|
255
|
+
})
|
|
256
|
+
.catch(err => {
|
|
257
|
+
console.error('Failed to fetch messages:', err);
|
|
258
|
+
throw new StandardError(500, 'Failed to fetch messages');
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async fetchEvents(body = {}, cancelToken, headers = {}) {
|
|
262
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
263
|
+
throw new StandardError(400, 'Company ID is required');
|
|
264
|
+
}
|
|
265
|
+
return this._fetch('POST', `/company/${this.companyId}/events`, JSON.stringify(body), cancelToken, {
|
|
266
|
+
...headers,
|
|
267
|
+
'Content-Type': 'application/json',
|
|
268
|
+
})
|
|
269
|
+
.then(response => {
|
|
270
|
+
if (!response || !Array.isArray(response)) {
|
|
271
|
+
throw new StandardError(400, 'Invalid events response');
|
|
272
|
+
}
|
|
273
|
+
return response;
|
|
274
|
+
})
|
|
275
|
+
.catch(err => {
|
|
276
|
+
console.error('Failed to fetch events:', err);
|
|
277
|
+
throw new StandardError(500, 'Failed to fetch events');
|
|
278
|
+
});
|
|
115
279
|
}
|
|
116
|
-
|
|
117
|
-
|
|
280
|
+
fetchMonths(body = {}, cancelToken, headers = {}) {
|
|
281
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
282
|
+
throw new StandardError(400, 'Company ID is required');
|
|
283
|
+
}
|
|
284
|
+
return this._fetch('POST', `/company/${this.companyId}/months`, JSON.stringify(body), cancelToken, {
|
|
285
|
+
...headers,
|
|
286
|
+
'Content-Type': 'application/json',
|
|
287
|
+
})
|
|
288
|
+
.then(response => {
|
|
289
|
+
if (!response || !Array.isArray(response)) {
|
|
290
|
+
throw new StandardError(400, 'Invalid months response');
|
|
291
|
+
}
|
|
292
|
+
return response;
|
|
293
|
+
})
|
|
294
|
+
.catch(err => {
|
|
295
|
+
console.error('Failed to fetch months:', err);
|
|
296
|
+
throw new StandardError(500, 'Failed to fetch months');
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
fetchBills(body = {}, cancelToken, headers = {}) {
|
|
300
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
301
|
+
throw new StandardError(400, 'Company ID is required');
|
|
302
|
+
}
|
|
303
|
+
return this._fetch('POST', `/company/${this.companyId}/bills`, JSON.stringify(body), cancelToken, {
|
|
304
|
+
...headers,
|
|
305
|
+
'Content-Type': 'application/json',
|
|
306
|
+
})
|
|
307
|
+
.then(response => {
|
|
308
|
+
if (!response || !Array.isArray(response)) {
|
|
309
|
+
throw new StandardError(400, 'Invalid bills response');
|
|
310
|
+
}
|
|
311
|
+
return response;
|
|
312
|
+
})
|
|
313
|
+
.catch(err => {
|
|
314
|
+
console.error('Failed to fetch bills:', err);
|
|
315
|
+
throw new StandardError(500, 'Failed to fetch bills');
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
fetchRoutes(body = {}, cancelToken, headers = {}) {
|
|
319
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
320
|
+
throw new StandardError(400, 'Company ID is required');
|
|
321
|
+
}
|
|
322
|
+
return this._fetch('POST', `/company/${this.companyId}/routes`, JSON.stringify(body), cancelToken, {
|
|
323
|
+
...headers,
|
|
324
|
+
'Content-Type': 'application/json',
|
|
325
|
+
})
|
|
326
|
+
.then(response => {
|
|
327
|
+
if (!response || !Array.isArray(response)) {
|
|
328
|
+
throw new StandardError(400, 'Invalid routes response');
|
|
329
|
+
}
|
|
330
|
+
return response;
|
|
331
|
+
})
|
|
332
|
+
.catch(err => {
|
|
333
|
+
console.error('Failed to fetch routes:', err);
|
|
334
|
+
throw new StandardError(500, 'Failed to fetch routes');
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
fetchTeams(body = {}, cancelToken, headers = {}) {
|
|
338
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
339
|
+
throw new StandardError(400, 'Company ID is required');
|
|
340
|
+
}
|
|
341
|
+
return this._fetch('POST', `/company/${this.companyId}/teams`, JSON.stringify(body), cancelToken, {
|
|
342
|
+
...headers,
|
|
343
|
+
'Content-Type': 'application/json',
|
|
344
|
+
})
|
|
345
|
+
.then(response => {
|
|
346
|
+
if (!response || !Array.isArray(response)) {
|
|
347
|
+
throw new StandardError(400, 'Invalid teams response');
|
|
348
|
+
}
|
|
349
|
+
return response;
|
|
350
|
+
})
|
|
351
|
+
.catch(err => {
|
|
352
|
+
console.error('Failed to fetch teams:', err);
|
|
353
|
+
throw new StandardError(500, 'Failed to fetch teams');
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
fetchSessions(body = {}, cancelToken, headers = {}) {
|
|
357
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
358
|
+
throw new StandardError(400, 'Company ID is required');
|
|
359
|
+
}
|
|
360
|
+
return this._fetch('POST', `/company/${this.companyId}/sessions`, JSON.stringify(body), cancelToken, {
|
|
361
|
+
...headers,
|
|
362
|
+
'Content-Type': 'application/json',
|
|
363
|
+
})
|
|
364
|
+
.then(response => {
|
|
365
|
+
if (!response || !Array.isArray(response)) {
|
|
366
|
+
throw new StandardError(400, 'Invalid sessions response');
|
|
367
|
+
}
|
|
368
|
+
return response;
|
|
369
|
+
})
|
|
370
|
+
.catch(err => {
|
|
371
|
+
console.error('Failed to fetch sessions:', err);
|
|
372
|
+
throw new StandardError(500, 'Failed to fetch sessions');
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
sendEmail(emailId, formData, progressUpdateCallback) {
|
|
376
|
+
try {
|
|
377
|
+
return this.post(`/email/${emailId}`, formData, undefined, {}, progressUpdateCallback);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
throw new StandardError(500, `Failed to send email: ${error}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
createBill(data, cancelToken) {
|
|
384
|
+
if (!this.companyId || this.companyId.trim() === '') {
|
|
385
|
+
throw new StandardError(400, 'Company ID is required');
|
|
386
|
+
}
|
|
387
|
+
const body = {
|
|
388
|
+
events: data.line_items.filter(item => item.line_item_type === 'EVENT').map(item => item.id),
|
|
389
|
+
months: data.line_items.filter(item => item.line_item_type === 'SERVICE_MONTH').map(item => item.id),
|
|
390
|
+
line_items: data.line_items.filter(item => item.line_item_type === 'CUSTOM'),
|
|
391
|
+
manual_emails: data.custom_emails,
|
|
392
|
+
users: data.users,
|
|
393
|
+
due_date: data.due_date,
|
|
394
|
+
};
|
|
395
|
+
try {
|
|
396
|
+
return this._xhr('POST', `/company/${this.companyId}/bill`, JSON.stringify(body), cancelToken, {
|
|
397
|
+
'Content-Type': 'application/json',
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
throw new StandardError(500, `Failed to create bill: ${error}`);
|
|
402
|
+
}
|
|
118
403
|
}
|
|
119
404
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { AvroQueryClient } from '../../client/QueryClient';
|
|
2
|
+
import { StandardError } from '../../types/error';
|
|
3
|
+
AvroQueryClient.prototype._fetch = async function (method, path, body, cancelToken, headers = {}, isIdempotent = false, retryCount = 0) {
|
|
4
|
+
const checkCancelled = () => {
|
|
5
|
+
try {
|
|
6
|
+
if (cancelToken?.isCancelled()) {
|
|
7
|
+
throw new StandardError(0, 'Request cancelled');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw new StandardError(0, `Error checking cancellation: ${error.message}`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
try {
|
|
15
|
+
checkCancelled();
|
|
16
|
+
const token = await this.config.authManager.accessToken();
|
|
17
|
+
checkCancelled();
|
|
18
|
+
const url = this.config.baseUrl + path;
|
|
19
|
+
const requestHeaders = {
|
|
20
|
+
Authorization: `Bearer ${token}`,
|
|
21
|
+
...headers,
|
|
22
|
+
};
|
|
23
|
+
const options = {
|
|
24
|
+
method,
|
|
25
|
+
headers: requestHeaders,
|
|
26
|
+
body,
|
|
27
|
+
};
|
|
28
|
+
const response = await fetch(url, options);
|
|
29
|
+
if (response.ok) {
|
|
30
|
+
const text = await response.text();
|
|
31
|
+
if (!text) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
if (response.status === 401 && this.config.authManager.refreshTokens && retryCount === 0) {
|
|
37
|
+
await this.config.authManager.refreshTokens();
|
|
38
|
+
return this._fetch(method, path, body, cancelToken, headers, isIdempotent, 1);
|
|
39
|
+
}
|
|
40
|
+
if (retryCount < this.config.maxRetries) {
|
|
41
|
+
const delay = this.getDelay(this.config.retryStrategy, retryCount);
|
|
42
|
+
await this.sleep(delay);
|
|
43
|
+
return this._fetch(method, path, body, cancelToken, headers, isIdempotent, retryCount + 1);
|
|
44
|
+
}
|
|
45
|
+
let errorMessage = response.statusText;
|
|
46
|
+
try {
|
|
47
|
+
const parsedError = await response.json();
|
|
48
|
+
errorMessage = parsedError.message ?? parsedError.msg ?? errorMessage;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.error('Ignoring:', e);
|
|
52
|
+
}
|
|
53
|
+
throw new StandardError(response.status, errorMessage);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof StandardError) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
+
throw new StandardError(0, `Request failed: ${message}`);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AvroQueryClient } from '../../client/QueryClient';
|
|
2
|
+
AvroQueryClient.prototype.getDelay = function (strategy, attempt) {
|
|
3
|
+
if (typeof strategy === 'function') {
|
|
4
|
+
return strategy(attempt);
|
|
5
|
+
}
|
|
6
|
+
else if (strategy === 'fixed') {
|
|
7
|
+
return 1000;
|
|
8
|
+
}
|
|
9
|
+
else if (strategy === 'exponential') {
|
|
10
|
+
return Math.pow(2, attempt) * 100;
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Invalid retry strategy: ${strategy}`);
|
|
13
|
+
};
|
|
14
|
+
AvroQueryClient.prototype.sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AvroQueryClient } from '../../client/QueryClient';
|
|
2
|
+
import { StandardError } from '../../types/error';
|
|
3
|
+
AvroQueryClient.prototype._xhr = async function (method, path, body, cancelToken, headers = {}, isIdempotent = false, retryCount = 0, progressUpdateCallback) {
|
|
4
|
+
const checkCancelled = () => {
|
|
5
|
+
if (cancelToken?.isCancelled()) {
|
|
6
|
+
throw new StandardError(0, 'Request cancelled');
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
10
|
+
try {
|
|
11
|
+
checkCancelled();
|
|
12
|
+
const token = await this.config.authManager.accessToken();
|
|
13
|
+
checkCancelled();
|
|
14
|
+
const result = await new Promise((resolve, reject) => {
|
|
15
|
+
const xhr = new XMLHttpRequest();
|
|
16
|
+
const url = this.config.baseUrl + path;
|
|
17
|
+
xhr.open(method, url, true);
|
|
18
|
+
if (token)
|
|
19
|
+
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
|
|
20
|
+
Object.entries(headers).forEach(([key, value]) => xhr.setRequestHeader(key, value));
|
|
21
|
+
xhr.onload = () => {
|
|
22
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
23
|
+
try {
|
|
24
|
+
const responseText = xhr.responseText;
|
|
25
|
+
if (!responseText) {
|
|
26
|
+
return resolve(undefined);
|
|
27
|
+
}
|
|
28
|
+
resolve(JSON.parse(responseText));
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
reject(new StandardError(0, `Failed to parse successful response: ${e.message}`));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
let msg = xhr.responseText || xhr.statusText;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(xhr.responseText);
|
|
38
|
+
msg = parsed.message ?? parsed.msg ?? msg;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.error('Ignoring:', e);
|
|
42
|
+
}
|
|
43
|
+
reject(new StandardError(xhr.status, msg));
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
xhr.onerror = () => reject(new StandardError(0, 'Network Error'));
|
|
47
|
+
if (this.config.timeout) {
|
|
48
|
+
xhr.timeout = this.config.timeout;
|
|
49
|
+
xhr.ontimeout = () => reject(new StandardError(0, 'Request timed out'));
|
|
50
|
+
}
|
|
51
|
+
if (progressUpdateCallback && xhr.upload) {
|
|
52
|
+
xhr.upload.onprogress = (event) => {
|
|
53
|
+
if (event.lengthComputable) {
|
|
54
|
+
progressUpdateCallback(event.loaded, event.total);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
xhr.send(body);
|
|
59
|
+
});
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (!(error instanceof StandardError)) {
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
throw new StandardError(0, `An unexpected error occurred: ${message}`);
|
|
66
|
+
}
|
|
67
|
+
if (error.status === 401 && this.config.authManager.refreshTokens && attempt === 0) {
|
|
68
|
+
try {
|
|
69
|
+
await this.config.authManager.refreshTokens();
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
catch (refreshError) {
|
|
73
|
+
throw new StandardError(401, 'Unauthorized (refresh failed)');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (attempt >= this.config.maxRetries) {
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
const delay = this.getDelay(this.config.retryStrategy, attempt);
|
|
80
|
+
await this.sleep(delay);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new StandardError(0, 'Request failed after maximum retries.');
|
|
84
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|