kmod-cli 1.8.0 → 1.8.2
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.
|
@@ -18,23 +18,23 @@ import Cookies from 'js-cookie';
|
|
|
18
18
|
export interface Pagination {
|
|
19
19
|
current?: number;
|
|
20
20
|
pageSize?: number;
|
|
21
|
-
mode?:
|
|
21
|
+
mode?: "server" | "client";
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface Sorter {
|
|
25
25
|
field: string;
|
|
26
|
-
order:
|
|
26
|
+
order: "asc" | "desc";
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export type FilterOperator =
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
29
|
+
export type FilterOperator =
|
|
30
|
+
| "eq"
|
|
31
|
+
| "ne"
|
|
32
|
+
| "lt"
|
|
33
|
+
| "lte"
|
|
34
|
+
| "gt"
|
|
35
|
+
| "gte"
|
|
36
|
+
| "in"
|
|
37
|
+
| "contains";
|
|
38
38
|
|
|
39
39
|
export interface Filter {
|
|
40
40
|
field: string;
|
|
@@ -93,7 +93,7 @@ export interface DeleteManyParams {
|
|
|
93
93
|
|
|
94
94
|
export interface CustomParams extends AxiosRequestConfig {
|
|
95
95
|
url?: string;
|
|
96
|
-
method?:
|
|
96
|
+
method?: "get" | "post" | "put" | "patch" | "delete";
|
|
97
97
|
payload?: any;
|
|
98
98
|
query?: Record<string, any>;
|
|
99
99
|
headers?: Record<string, string>;
|
|
@@ -162,6 +162,11 @@ export interface UseMutationOptions<TData = any> {
|
|
|
162
162
|
|
|
163
163
|
export type Payload = any;
|
|
164
164
|
|
|
165
|
+
type ApiError = {
|
|
166
|
+
message: string;
|
|
167
|
+
statusCode?: number;
|
|
168
|
+
};
|
|
169
|
+
|
|
165
170
|
// ============ UTILITY FUNCTIONS ============
|
|
166
171
|
|
|
167
172
|
/**
|
|
@@ -172,14 +177,14 @@ function normalizeResponseData<T>(response: any): T[] {
|
|
|
172
177
|
if (Array.isArray(response)) {
|
|
173
178
|
return response;
|
|
174
179
|
}
|
|
175
|
-
|
|
180
|
+
|
|
176
181
|
// Case 2: Object with 'data' property
|
|
177
|
-
if (response && typeof response ===
|
|
178
|
-
return response
|
|
182
|
+
if (response && typeof response === "object") {
|
|
183
|
+
return response;
|
|
179
184
|
}
|
|
180
|
-
|
|
185
|
+
|
|
181
186
|
// Fallback: empty array
|
|
182
|
-
console.warn(
|
|
187
|
+
console.warn("Unable to extract array data from response:", response);
|
|
183
188
|
return [];
|
|
184
189
|
}
|
|
185
190
|
|
|
@@ -188,19 +193,19 @@ function normalizeResponseData<T>(response: any): T[] {
|
|
|
188
193
|
*/
|
|
189
194
|
function extractTotalCount(response: any, dataLength: number): number {
|
|
190
195
|
// Check headers first
|
|
191
|
-
if (response.headers?.[
|
|
192
|
-
const count = parseInt(response.headers[
|
|
196
|
+
if (response.headers?.["x-total-count"]) {
|
|
197
|
+
const count = parseInt(response.headers["x-total-count"], 10);
|
|
193
198
|
if (!isNaN(count)) return count;
|
|
194
199
|
}
|
|
195
|
-
|
|
200
|
+
|
|
196
201
|
// Check response body
|
|
197
202
|
const data = response.data;
|
|
198
|
-
if (data && typeof data ===
|
|
199
|
-
if (typeof data.total ===
|
|
200
|
-
if (typeof data.totalCount ===
|
|
201
|
-
if (typeof data.count ===
|
|
203
|
+
if (data && typeof data === "object") {
|
|
204
|
+
if (typeof data.total === "number") return data.total;
|
|
205
|
+
if (typeof data.totalCount === "number") return data.totalCount;
|
|
206
|
+
if (typeof data.count === "number") return data.count;
|
|
202
207
|
}
|
|
203
|
-
|
|
208
|
+
|
|
204
209
|
// Fallback to data length
|
|
205
210
|
return dataLength;
|
|
206
211
|
}
|
|
@@ -217,31 +222,33 @@ class DataProvider {
|
|
|
217
222
|
private options: Required<DataProviderOptions>;
|
|
218
223
|
|
|
219
224
|
constructor(
|
|
220
|
-
httpClient: AxiosInstance = axios.create(),
|
|
221
|
-
options: DataProviderOptions = {}
|
|
225
|
+
httpClient: AxiosInstance = axios.create(),
|
|
226
|
+
options: DataProviderOptions = {},
|
|
222
227
|
) {
|
|
223
228
|
this.httpClient = httpClient;
|
|
224
229
|
|
|
225
|
-
const baseURL = httpClient.defaults.baseURL ||
|
|
226
|
-
this.apiUrl = baseURL.endsWith(
|
|
227
|
-
|
|
230
|
+
const baseURL = httpClient.defaults.baseURL || "";
|
|
231
|
+
this.apiUrl = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
232
|
+
|
|
228
233
|
if (!this.apiUrl) {
|
|
229
|
-
console.warn(
|
|
234
|
+
console.warn(
|
|
235
|
+
"[DataProvider] No baseURL found in httpClient. Please set baseURL when creating httpClient.",
|
|
236
|
+
);
|
|
230
237
|
}
|
|
231
|
-
|
|
238
|
+
|
|
232
239
|
this.cache = new Map();
|
|
233
240
|
this.options = {
|
|
234
241
|
cacheTime: 5 * 60 * 1000,
|
|
235
242
|
retryCount: 1,
|
|
236
243
|
retryDelay: 1000,
|
|
237
244
|
debug: false,
|
|
238
|
-
...options
|
|
245
|
+
...options,
|
|
239
246
|
};
|
|
240
247
|
}
|
|
241
248
|
|
|
242
249
|
private log(message: string, data?: any): void {
|
|
243
250
|
if (this.options.debug) {
|
|
244
|
-
console.log(`[DataProvider] ${message}`, data || '');
|
|
251
|
+
// console.log(`[DataProvider] ${message}`, data || '');
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
254
|
|
|
@@ -253,43 +260,46 @@ class DataProvider {
|
|
|
253
260
|
private getCache<T = any>(key: string): T | null {
|
|
254
261
|
const cached = this.cache.get(key);
|
|
255
262
|
if (!cached) return null;
|
|
256
|
-
|
|
263
|
+
|
|
257
264
|
const now = Date.now();
|
|
258
265
|
if (now - cached.timestamp > this.options.cacheTime) {
|
|
259
266
|
this.cache.delete(key);
|
|
260
|
-
this.log('Cache expired', key);
|
|
267
|
+
// this.log('Cache expired', key);
|
|
261
268
|
return null;
|
|
262
269
|
}
|
|
263
|
-
|
|
264
|
-
this.log('Cache hit', key);
|
|
270
|
+
|
|
271
|
+
// this.log('Cache hit', key);
|
|
265
272
|
return cached.data as T;
|
|
266
273
|
}
|
|
267
274
|
|
|
268
275
|
private setCache<T = any>(key: string, data: T): void {
|
|
269
276
|
this.cache.set(key, {
|
|
270
277
|
data,
|
|
271
|
-
timestamp: Date.now()
|
|
278
|
+
timestamp: Date.now(),
|
|
272
279
|
});
|
|
273
|
-
this.log('Cache set', key);
|
|
280
|
+
// this.log('Cache set', key);
|
|
274
281
|
}
|
|
275
282
|
|
|
276
283
|
public invalidateCache(resource: string, id?: string | number): void {
|
|
277
284
|
const keys = Array.from(this.cache.keys());
|
|
278
|
-
|
|
285
|
+
|
|
279
286
|
if (id !== undefined) {
|
|
280
287
|
// Invalidate specific item and related lists
|
|
281
|
-
keys.forEach(key => {
|
|
282
|
-
if (
|
|
288
|
+
keys.forEach((key) => {
|
|
289
|
+
if (
|
|
290
|
+
key.startsWith(`${resource}:`) ||
|
|
291
|
+
key.startsWith(`${resource}/${id}:`)
|
|
292
|
+
) {
|
|
283
293
|
this.cache.delete(key);
|
|
284
|
-
this.log('Cache invalidated', key);
|
|
294
|
+
// this.log('Cache invalidated', key);
|
|
285
295
|
}
|
|
286
296
|
});
|
|
287
297
|
} else {
|
|
288
298
|
// Invalidate entire resource
|
|
289
|
-
keys.forEach(key => {
|
|
299
|
+
keys.forEach((key) => {
|
|
290
300
|
if (key.startsWith(`${resource}:`)) {
|
|
291
301
|
this.cache.delete(key);
|
|
292
|
-
this.log('Cache invalidated', key);
|
|
302
|
+
// this.log('Cache invalidated', key);
|
|
293
303
|
}
|
|
294
304
|
});
|
|
295
305
|
}
|
|
@@ -297,371 +307,382 @@ class DataProvider {
|
|
|
297
307
|
|
|
298
308
|
public clearAllCache(): void {
|
|
299
309
|
this.cache.clear();
|
|
300
|
-
this.log('All cache cleared');
|
|
310
|
+
// this.log('All cache cleared');
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
// Retry logic
|
|
304
314
|
private async retryRequest<T>(
|
|
305
|
-
fn: () => Promise<T>,
|
|
306
|
-
retries: number = this.options.retryCount
|
|
315
|
+
fn: () => Promise<T>,
|
|
316
|
+
retries: number = this.options.retryCount,
|
|
307
317
|
): Promise<T> {
|
|
308
318
|
try {
|
|
309
319
|
return await fn();
|
|
310
320
|
} catch (error) {
|
|
311
321
|
if (retries <= 0) throw error;
|
|
312
|
-
|
|
322
|
+
|
|
313
323
|
// Don't retry on 4xx errors (client errors)
|
|
314
324
|
const axiosError = error as AxiosError;
|
|
315
325
|
if (
|
|
316
|
-
axiosError.response &&
|
|
317
|
-
axiosError.response.status >= 400 &&
|
|
326
|
+
axiosError.response &&
|
|
327
|
+
axiosError.response.status >= 400 &&
|
|
318
328
|
axiosError.response.status < 500
|
|
319
329
|
) {
|
|
320
330
|
throw error;
|
|
321
331
|
}
|
|
322
|
-
|
|
323
|
-
this.log(`Retrying... (${this.options.retryCount - retries + 1}/${this.options.retryCount})`);
|
|
324
|
-
|
|
325
|
-
await new Promise(resolve =>
|
|
326
|
-
setTimeout(resolve, this.options.retryDelay)
|
|
332
|
+
|
|
333
|
+
// this.log(`Retrying... (${this.options.retryCount - retries + 1}/${this.options.retryCount})`);
|
|
334
|
+
|
|
335
|
+
await new Promise((resolve) =>
|
|
336
|
+
setTimeout(resolve, this.options.retryDelay),
|
|
327
337
|
);
|
|
328
|
-
|
|
338
|
+
|
|
329
339
|
return this.retryRequest(fn, retries - 1);
|
|
330
340
|
}
|
|
331
341
|
}
|
|
332
342
|
|
|
333
343
|
// CRUD Methods
|
|
334
344
|
async getList<T = any>(
|
|
335
|
-
resource: string,
|
|
336
|
-
params: GetListParams = {},
|
|
337
|
-
useCache: boolean = true
|
|
345
|
+
resource: string,
|
|
346
|
+
params: GetListParams = {},
|
|
347
|
+
useCache: boolean = true,
|
|
338
348
|
): Promise<GetListResponse<T>> {
|
|
339
349
|
const cacheKey = this.getCacheKey(resource, params);
|
|
340
|
-
|
|
350
|
+
|
|
341
351
|
if (useCache) {
|
|
342
352
|
const cached = this.getCache<GetListResponse<T>>(cacheKey);
|
|
343
353
|
if (cached) return cached;
|
|
344
354
|
}
|
|
345
|
-
|
|
355
|
+
|
|
346
356
|
const { pagination, filters, sorters, meta } = params;
|
|
347
357
|
const url = `${this.apiUrl}/${resource}`;
|
|
348
358
|
const query: Record<string, any> = {};
|
|
349
|
-
|
|
359
|
+
|
|
350
360
|
if (pagination) {
|
|
351
361
|
const { current = 1, pageSize = 10 } = pagination;
|
|
352
362
|
query._start = (current - 1) * pageSize;
|
|
353
363
|
query._limit = pageSize;
|
|
354
364
|
}
|
|
355
|
-
|
|
365
|
+
|
|
356
366
|
if (sorters && sorters.length > 0) {
|
|
357
|
-
query._sort = sorters.map(s => s.field).join(
|
|
358
|
-
query._order = sorters.map(s => s.order).join(
|
|
367
|
+
query._sort = sorters.map((s) => s.field).join(",");
|
|
368
|
+
query._order = sorters.map((s) => s.order).join(",");
|
|
359
369
|
}
|
|
360
|
-
|
|
370
|
+
|
|
361
371
|
if (filters && filters.length > 0) {
|
|
362
|
-
filters.forEach(filter => {
|
|
372
|
+
filters.forEach((filter) => {
|
|
363
373
|
const { field, operator, value } = filter;
|
|
364
374
|
switch (operator) {
|
|
365
|
-
case
|
|
366
|
-
query[field] = value;
|
|
375
|
+
case "eq":
|
|
376
|
+
query[field] = value;
|
|
367
377
|
break;
|
|
368
|
-
case
|
|
369
|
-
query[`${field}_ne`] = value;
|
|
378
|
+
case "ne":
|
|
379
|
+
query[`${field}_ne`] = value;
|
|
370
380
|
break;
|
|
371
|
-
case
|
|
372
|
-
query[`${field}_lt`] = value;
|
|
381
|
+
case "lt":
|
|
382
|
+
query[`${field}_lt`] = value;
|
|
373
383
|
break;
|
|
374
|
-
case
|
|
375
|
-
query[`${field}_lte`] = value;
|
|
384
|
+
case "lte":
|
|
385
|
+
query[`${field}_lte`] = value;
|
|
376
386
|
break;
|
|
377
|
-
case
|
|
378
|
-
query[`${field}_gt`] = value;
|
|
387
|
+
case "gt":
|
|
388
|
+
query[`${field}_gt`] = value;
|
|
379
389
|
break;
|
|
380
|
-
case
|
|
381
|
-
query[`${field}_gte`] = value;
|
|
390
|
+
case "gte":
|
|
391
|
+
query[`${field}_gte`] = value;
|
|
382
392
|
break;
|
|
383
|
-
case
|
|
384
|
-
query[`${field}_in`] = Array.isArray(value)
|
|
393
|
+
case "in":
|
|
394
|
+
query[`${field}_in`] = Array.isArray(value)
|
|
395
|
+
? value.join(",")
|
|
396
|
+
: value;
|
|
385
397
|
break;
|
|
386
|
-
case
|
|
387
|
-
query[`${field}_like`] = value;
|
|
398
|
+
case "contains":
|
|
399
|
+
query[`${field}_like`] = value;
|
|
388
400
|
break;
|
|
389
401
|
}
|
|
390
402
|
});
|
|
391
403
|
}
|
|
392
|
-
|
|
404
|
+
|
|
393
405
|
try {
|
|
394
406
|
const result = await this.retryRequest(async () => {
|
|
395
|
-
this.log(`GET ${url}`, query);
|
|
396
|
-
|
|
397
|
-
const response = await this.httpClient.get(url, {
|
|
407
|
+
// this.log(`GET ${url}`, query);
|
|
408
|
+
|
|
409
|
+
const response = await this.httpClient.get(url, {
|
|
398
410
|
params: query,
|
|
399
|
-
...meta
|
|
411
|
+
...meta,
|
|
400
412
|
});
|
|
401
|
-
|
|
413
|
+
|
|
402
414
|
const data = normalizeResponseData<T>(response.data);
|
|
403
415
|
const total = extractTotalCount(response, data.length);
|
|
404
|
-
|
|
405
|
-
this.log(`Response: ${data.length} items, total: ${total}`);
|
|
406
|
-
|
|
416
|
+
|
|
417
|
+
// this.log(`Response: ${data.length} items, total: ${total}`);
|
|
418
|
+
|
|
407
419
|
return { data, total };
|
|
408
420
|
});
|
|
409
|
-
|
|
421
|
+
|
|
410
422
|
if (useCache) {
|
|
411
423
|
this.setCache(cacheKey, result);
|
|
412
424
|
}
|
|
413
|
-
|
|
425
|
+
|
|
414
426
|
return result;
|
|
415
427
|
} catch (error) {
|
|
416
|
-
this.
|
|
417
|
-
|
|
428
|
+
const apiError = this.handleError(error);
|
|
429
|
+
return Promise.reject(apiError);
|
|
418
430
|
}
|
|
419
431
|
}
|
|
420
432
|
|
|
421
433
|
async getOne<T = any>(
|
|
422
|
-
resource: string,
|
|
423
|
-
params: GetOneParams,
|
|
424
|
-
useCache: boolean = true
|
|
434
|
+
resource: string,
|
|
435
|
+
params: GetOneParams,
|
|
436
|
+
useCache: boolean = true,
|
|
425
437
|
): Promise<GetOneResponse<T>> {
|
|
426
438
|
const { id, meta } = params;
|
|
427
439
|
const cacheKey = this.getCacheKey(`${resource}/${id}`, {});
|
|
428
|
-
|
|
440
|
+
|
|
429
441
|
if (useCache) {
|
|
430
442
|
const cached = this.getCache<GetOneResponse<T>>(cacheKey);
|
|
431
443
|
if (cached) return cached;
|
|
432
444
|
}
|
|
433
|
-
|
|
445
|
+
|
|
434
446
|
const url = `${this.apiUrl}/${resource}/${id}`;
|
|
435
|
-
|
|
447
|
+
|
|
436
448
|
try {
|
|
437
449
|
const result = await this.retryRequest(async () => {
|
|
438
|
-
this.log(`GET ${url}`);
|
|
450
|
+
// this.log(`GET ${url}`);
|
|
439
451
|
const response = await this.httpClient.get<T>(url, meta);
|
|
440
|
-
|
|
452
|
+
|
|
441
453
|
// Handle wrapped response
|
|
442
|
-
const data = (response.data as any) || response
|
|
443
|
-
|
|
454
|
+
const data = (response.data as any) || response;
|
|
455
|
+
|
|
444
456
|
return { data };
|
|
445
457
|
});
|
|
446
|
-
|
|
458
|
+
|
|
447
459
|
if (useCache) {
|
|
448
460
|
this.setCache(cacheKey, result);
|
|
449
461
|
}
|
|
450
|
-
|
|
462
|
+
|
|
451
463
|
return result;
|
|
452
464
|
} catch (error) {
|
|
453
|
-
this.
|
|
454
|
-
|
|
465
|
+
const apiError = this.handleError(error);
|
|
466
|
+
return Promise.reject(apiError);
|
|
455
467
|
}
|
|
456
468
|
}
|
|
457
469
|
|
|
458
470
|
async getMany<T = any>(
|
|
459
|
-
resource: string,
|
|
460
|
-
params: GetManyParams,
|
|
461
|
-
useCache: boolean = true
|
|
471
|
+
resource: string,
|
|
472
|
+
params: GetManyParams,
|
|
473
|
+
useCache: boolean = true,
|
|
462
474
|
): Promise<GetManyResponse<T>> {
|
|
463
475
|
const { ids, meta } = params;
|
|
464
476
|
const url = `${this.apiUrl}/${resource}`;
|
|
465
|
-
|
|
477
|
+
|
|
466
478
|
try {
|
|
467
479
|
const result = await this.retryRequest(async () => {
|
|
468
|
-
this.log(`GET ${url} with ids`, ids);
|
|
469
|
-
|
|
480
|
+
// this.log(`GET ${url} with ids`, ids);
|
|
481
|
+
|
|
470
482
|
const response = await this.httpClient.get(url, {
|
|
471
483
|
params: { id: ids },
|
|
472
|
-
...meta
|
|
484
|
+
...meta,
|
|
473
485
|
});
|
|
474
|
-
|
|
486
|
+
|
|
475
487
|
const data = normalizeResponseData<T>(response.data);
|
|
476
|
-
|
|
488
|
+
|
|
477
489
|
return { data };
|
|
478
490
|
});
|
|
479
|
-
|
|
491
|
+
|
|
480
492
|
return result;
|
|
481
493
|
} catch (error) {
|
|
482
|
-
this.
|
|
483
|
-
|
|
494
|
+
const apiError = this.handleError(error);
|
|
495
|
+
return Promise.reject(apiError);
|
|
484
496
|
}
|
|
485
497
|
}
|
|
486
498
|
|
|
487
499
|
async create<T = any, V = any>(
|
|
488
|
-
resource: string,
|
|
489
|
-
params: CreateParams<V
|
|
500
|
+
resource: string,
|
|
501
|
+
params: CreateParams<V>,
|
|
490
502
|
): Promise<CreateResponse<T>> {
|
|
491
503
|
const { variables, meta } = params;
|
|
492
504
|
const url = `${this.apiUrl}/${resource}`;
|
|
493
|
-
|
|
505
|
+
|
|
494
506
|
try {
|
|
495
507
|
const result = await this.retryRequest(async () => {
|
|
496
|
-
this.log(`POST ${url}`, variables);
|
|
508
|
+
// this.log(`POST ${url}`, variables);
|
|
497
509
|
const response = await this.httpClient.post<T>(url, variables, meta);
|
|
498
|
-
|
|
510
|
+
|
|
499
511
|
// Handle wrapped response
|
|
500
|
-
const data = (response.data as any) || response
|
|
501
|
-
|
|
512
|
+
const data = (response.data as any) || response;
|
|
513
|
+
|
|
502
514
|
return { data };
|
|
503
515
|
});
|
|
504
|
-
|
|
516
|
+
|
|
505
517
|
this.invalidateCache(resource);
|
|
506
518
|
return result;
|
|
507
519
|
} catch (error) {
|
|
508
|
-
this.
|
|
509
|
-
|
|
520
|
+
const apiError = this.handleError(error);
|
|
521
|
+
return Promise.reject(apiError);
|
|
510
522
|
}
|
|
511
523
|
}
|
|
512
524
|
|
|
513
525
|
async createMany<T = any, V = any>(
|
|
514
|
-
resource: string,
|
|
515
|
-
params: CreateManyParams<V
|
|
526
|
+
resource: string,
|
|
527
|
+
params: CreateManyParams<V>,
|
|
516
528
|
): Promise<GetManyResponse<T>> {
|
|
517
529
|
const { variables, meta } = params;
|
|
518
|
-
|
|
530
|
+
|
|
519
531
|
try {
|
|
520
532
|
const result = await this.retryRequest(async () => {
|
|
521
|
-
this.log(`POST MANY ${this.apiUrl}/${resource}`, variables);
|
|
522
|
-
|
|
533
|
+
// this.log(`POST MANY ${this.apiUrl}/${resource}`, variables);
|
|
534
|
+
|
|
523
535
|
const responses = await Promise.all(
|
|
524
|
-
variables.map(variable =>
|
|
525
|
-
this.httpClient.post<T>(
|
|
526
|
-
|
|
536
|
+
variables.map((variable) =>
|
|
537
|
+
this.httpClient.post<T>(
|
|
538
|
+
`${this.apiUrl}/${resource}`,
|
|
539
|
+
variable,
|
|
540
|
+
meta,
|
|
541
|
+
),
|
|
542
|
+
),
|
|
527
543
|
);
|
|
528
|
-
|
|
529
|
-
const data = responses.map(r => (r.data as any)?.data || r.data);
|
|
530
|
-
|
|
544
|
+
|
|
545
|
+
const data = responses.map((r) => (r.data as any)?.data || r.data);
|
|
546
|
+
|
|
531
547
|
return { data };
|
|
532
548
|
});
|
|
533
|
-
|
|
549
|
+
|
|
534
550
|
this.invalidateCache(resource);
|
|
535
551
|
return result;
|
|
536
552
|
} catch (error) {
|
|
537
|
-
this.
|
|
538
|
-
|
|
553
|
+
const apiError = this.handleError(error);
|
|
554
|
+
return Promise.reject(apiError);
|
|
539
555
|
}
|
|
540
556
|
}
|
|
541
557
|
|
|
542
558
|
async update<T = any, V = any>(
|
|
543
|
-
resource: string,
|
|
544
|
-
params: UpdateParams<V
|
|
559
|
+
resource: string,
|
|
560
|
+
params: UpdateParams<V>,
|
|
545
561
|
): Promise<UpdateResponse<T>> {
|
|
546
562
|
const { id, variables, meta } = params;
|
|
547
563
|
const url = `${this.apiUrl}/${resource}/${id}`;
|
|
548
|
-
|
|
564
|
+
|
|
549
565
|
try {
|
|
550
566
|
const result = await this.retryRequest(async () => {
|
|
551
|
-
this.log(`PATCH ${url}`, variables);
|
|
567
|
+
// this.log(`PATCH ${url}`, variables);
|
|
552
568
|
const response = await this.httpClient.patch<T>(url, variables, meta);
|
|
553
|
-
|
|
569
|
+
|
|
554
570
|
// Handle wrapped response
|
|
555
|
-
const data = (response.data as any) || response
|
|
556
|
-
|
|
571
|
+
const data = (response.data as any) || response;
|
|
572
|
+
|
|
557
573
|
return { data };
|
|
558
574
|
});
|
|
559
|
-
|
|
575
|
+
|
|
560
576
|
this.invalidateCache(resource, id);
|
|
561
577
|
return result;
|
|
562
578
|
} catch (error) {
|
|
563
|
-
this.
|
|
564
|
-
|
|
579
|
+
const apiError = this.handleError(error);
|
|
580
|
+
return Promise.reject(apiError);
|
|
565
581
|
}
|
|
566
582
|
}
|
|
567
583
|
|
|
568
584
|
async updateMany<T = any, V = any>(
|
|
569
|
-
resource: string,
|
|
570
|
-
params: UpdateManyParams<V
|
|
585
|
+
resource: string,
|
|
586
|
+
params: UpdateManyParams<V>,
|
|
571
587
|
): Promise<GetManyResponse<T>> {
|
|
572
588
|
const { ids, variables, meta } = params;
|
|
573
|
-
|
|
589
|
+
|
|
574
590
|
try {
|
|
575
591
|
const result = await this.retryRequest(async () => {
|
|
576
|
-
this.log(`PATCH MANY ${this.apiUrl}/${resource}`, { ids, variables });
|
|
577
|
-
|
|
592
|
+
// this.log(`PATCH MANY ${this.apiUrl}/${resource}`, { ids, variables });
|
|
593
|
+
|
|
578
594
|
const responses = await Promise.all(
|
|
579
|
-
ids.map(id =>
|
|
595
|
+
ids.map((id) =>
|
|
580
596
|
this.httpClient.patch<T>(
|
|
581
597
|
`${this.apiUrl}/${resource}/${id}`,
|
|
582
598
|
variables,
|
|
583
|
-
meta
|
|
584
|
-
)
|
|
585
|
-
)
|
|
599
|
+
meta,
|
|
600
|
+
),
|
|
601
|
+
),
|
|
586
602
|
);
|
|
587
|
-
|
|
588
|
-
const data = responses.map(r => (r.data as any)?.data || r.data);
|
|
589
|
-
|
|
603
|
+
|
|
604
|
+
const data = responses.map((r) => (r.data as any)?.data || r.data);
|
|
605
|
+
|
|
590
606
|
return { data };
|
|
591
607
|
});
|
|
592
|
-
|
|
593
|
-
ids.forEach(id => this.invalidateCache(resource, id));
|
|
608
|
+
|
|
609
|
+
ids.forEach((id) => this.invalidateCache(resource, id));
|
|
594
610
|
return result;
|
|
595
611
|
} catch (error) {
|
|
596
|
-
this.
|
|
597
|
-
|
|
612
|
+
const apiError = this.handleError(error);
|
|
613
|
+
return Promise.reject(apiError);
|
|
598
614
|
}
|
|
599
615
|
}
|
|
600
616
|
|
|
601
617
|
async deleteOne<T = any>(
|
|
602
|
-
resource: string,
|
|
603
|
-
params: DeleteOneParams
|
|
618
|
+
resource: string,
|
|
619
|
+
params: DeleteOneParams,
|
|
604
620
|
): Promise<DeleteResponse<T>> {
|
|
605
621
|
const { id, meta } = params;
|
|
606
622
|
const url = `${this.apiUrl}/${resource}/${id}`;
|
|
607
|
-
|
|
623
|
+
|
|
608
624
|
try {
|
|
609
625
|
const result = await this.retryRequest(async () => {
|
|
610
|
-
this.log(`DELETE ${url}`);
|
|
626
|
+
// this.log(`DELETE ${url}`);
|
|
611
627
|
const response = await this.httpClient.delete<T>(url, meta);
|
|
612
|
-
|
|
628
|
+
|
|
613
629
|
// Handle wrapped response
|
|
614
|
-
const data = (response.data as any) || response
|
|
615
|
-
|
|
630
|
+
const data = (response.data as any) || response;
|
|
631
|
+
|
|
616
632
|
return { data };
|
|
617
633
|
});
|
|
618
|
-
|
|
634
|
+
|
|
619
635
|
this.invalidateCache(resource, id);
|
|
620
636
|
return result;
|
|
621
637
|
} catch (error) {
|
|
622
|
-
this.
|
|
623
|
-
|
|
638
|
+
const apiError = this.handleError(error);
|
|
639
|
+
return Promise.reject(apiError);
|
|
624
640
|
}
|
|
625
641
|
}
|
|
626
642
|
|
|
627
643
|
async deleteMany<T = any>(
|
|
628
|
-
resource: string,
|
|
629
|
-
params: DeleteManyParams
|
|
644
|
+
resource: string,
|
|
645
|
+
params: DeleteManyParams,
|
|
630
646
|
): Promise<GetManyResponse<T>> {
|
|
631
647
|
const { ids, meta } = params;
|
|
632
|
-
|
|
648
|
+
|
|
633
649
|
try {
|
|
634
650
|
const result = await this.retryRequest(async () => {
|
|
635
|
-
this.log(`DELETE MANY ${this.apiUrl}/${resource}`, ids);
|
|
636
|
-
|
|
651
|
+
// this.log(`DELETE MANY ${this.apiUrl}/${resource}`, ids);
|
|
652
|
+
|
|
637
653
|
const responses = await Promise.all(
|
|
638
|
-
ids.map(id =>
|
|
639
|
-
this.httpClient.delete<T>(`${this.apiUrl}/${resource}/${id}`, meta)
|
|
640
|
-
)
|
|
654
|
+
ids.map((id) =>
|
|
655
|
+
this.httpClient.delete<T>(`${this.apiUrl}/${resource}/${id}`, meta),
|
|
656
|
+
),
|
|
641
657
|
);
|
|
642
|
-
|
|
643
|
-
const data = responses.map(r => (r.data as any)?.data || r.data);
|
|
644
|
-
|
|
658
|
+
|
|
659
|
+
const data = responses.map((r) => (r.data as any)?.data || r.data);
|
|
660
|
+
|
|
645
661
|
return { data };
|
|
646
662
|
});
|
|
647
|
-
|
|
648
|
-
ids.forEach(id => this.invalidateCache(resource, id));
|
|
663
|
+
|
|
664
|
+
ids.forEach((id) => this.invalidateCache(resource, id));
|
|
649
665
|
return result;
|
|
650
666
|
} catch (error) {
|
|
651
|
-
this.
|
|
652
|
-
|
|
667
|
+
const apiError = this.handleError(error);
|
|
668
|
+
return Promise.reject(apiError);
|
|
653
669
|
}
|
|
654
670
|
}
|
|
655
671
|
|
|
656
672
|
async custom<T = any>(params: CustomParams): Promise<CustomResponse<T>> {
|
|
657
|
-
const { url
|
|
673
|
+
const { url, method = "get", payload, query, headers } = params;
|
|
658
674
|
|
|
659
|
-
if (!url)
|
|
675
|
+
if (!url) {
|
|
676
|
+
return Promise.reject({
|
|
677
|
+
message: "No url provided",
|
|
678
|
+
statusCode: 0,
|
|
679
|
+
});
|
|
680
|
+
}
|
|
660
681
|
try {
|
|
661
682
|
return await this.retryRequest(async () => {
|
|
662
683
|
// if url is not absolute, assume it's a relative path
|
|
663
|
-
const requestUrl = url.startsWith(
|
|
664
|
-
this.log(`${method.toUpperCase()} ${requestUrl}`, { payload, query });
|
|
684
|
+
const requestUrl = url.startsWith("http") ? url : url;
|
|
685
|
+
// this.log(`${method.toUpperCase()} ${requestUrl}`, { payload, query });
|
|
665
686
|
|
|
666
687
|
const response = await this.httpClient<T>({
|
|
667
688
|
url: requestUrl,
|
|
@@ -671,40 +692,38 @@ class DataProvider {
|
|
|
671
692
|
headers,
|
|
672
693
|
});
|
|
673
694
|
|
|
674
|
-
|
|
675
695
|
return { data: (response?.data as any) || response };
|
|
676
696
|
});
|
|
677
697
|
} catch (error) {
|
|
678
|
-
this.
|
|
679
|
-
|
|
698
|
+
const apiError = this.handleError(error);
|
|
699
|
+
return Promise.reject(apiError);
|
|
680
700
|
}
|
|
681
701
|
}
|
|
682
702
|
|
|
683
703
|
private handleError(error: unknown): DataProviderError {
|
|
684
704
|
const axiosError = error as AxiosError<any>;
|
|
685
|
-
|
|
705
|
+
|
|
686
706
|
if (axiosError.response) {
|
|
687
|
-
const
|
|
688
|
-
|
|
707
|
+
const data = axiosError.response.data;
|
|
708
|
+
|
|
709
|
+
this.log("API ERROR", {
|
|
710
|
+
status: axiosError.response.status,
|
|
711
|
+
data,
|
|
712
|
+
});
|
|
713
|
+
|
|
689
714
|
return {
|
|
690
|
-
message:
|
|
691
|
-
responseData?.error ||
|
|
692
|
-
axiosError.message ||
|
|
693
|
-
'Request failed',
|
|
715
|
+
message: data?.message || data?.error || "Request failed",
|
|
694
716
|
statusCode: axiosError.response.status,
|
|
695
|
-
errors: responseData?.errors || responseData?.details,
|
|
696
|
-
};
|
|
697
|
-
} else if (axiosError.request) {
|
|
698
|
-
return {
|
|
699
|
-
message: 'Network error - no response received',
|
|
700
|
-
statusCode: 0,
|
|
701
717
|
};
|
|
702
718
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
statusCode: 0
|
|
707
|
-
}
|
|
719
|
+
|
|
720
|
+
if (axiosError.request) {
|
|
721
|
+
this.log("NETWORK ERROR", axiosError.request);
|
|
722
|
+
return { message: "Network error", statusCode: 0 };
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
this.log("UNKNOWN ERROR", error);
|
|
726
|
+
return { message: "Unknown error", statusCode: 0 };
|
|
708
727
|
}
|
|
709
728
|
}
|
|
710
729
|
|
|
@@ -715,7 +734,7 @@ export const DataProviderContext = createContext<DataProvider | null>(null);
|
|
|
715
734
|
export function useList<T = any>(
|
|
716
735
|
resource: string,
|
|
717
736
|
params: GetListParams = {},
|
|
718
|
-
options: UseListOptions = {}
|
|
737
|
+
options: UseListOptions = {},
|
|
719
738
|
) {
|
|
720
739
|
const [data, setData] = useState<T[] | T | any | null>(null);
|
|
721
740
|
const [total, setTotal] = useState<number>(0);
|
|
@@ -723,20 +742,23 @@ export function useList<T = any>(
|
|
|
723
742
|
const [error, setError] = useState<DataProviderError | null>(null);
|
|
724
743
|
|
|
725
744
|
const dataProvider = useDataProvider();
|
|
726
|
-
|
|
745
|
+
|
|
727
746
|
const { refetchInterval, enabled = true } = options;
|
|
728
747
|
const paramsStr = JSON.stringify(params);
|
|
729
|
-
|
|
748
|
+
|
|
730
749
|
const refetch = useCallback(async () => {
|
|
731
750
|
if (!enabled) {
|
|
732
751
|
setLoading(false);
|
|
733
752
|
return;
|
|
734
753
|
}
|
|
735
|
-
|
|
754
|
+
|
|
736
755
|
try {
|
|
737
756
|
setLoading(true);
|
|
738
757
|
setError(null);
|
|
739
|
-
const result = await dataProvider.getList<T>(
|
|
758
|
+
const result = await dataProvider.getList<T>(
|
|
759
|
+
resource,
|
|
760
|
+
JSON.parse(paramsStr),
|
|
761
|
+
);
|
|
740
762
|
setData(result.data || []);
|
|
741
763
|
setTotal(result.total || 0);
|
|
742
764
|
} catch (err) {
|
|
@@ -747,40 +769,40 @@ export function useList<T = any>(
|
|
|
747
769
|
setLoading(false);
|
|
748
770
|
}
|
|
749
771
|
}, [dataProvider, resource, paramsStr, enabled]);
|
|
750
|
-
|
|
772
|
+
|
|
751
773
|
useEffect(() => {
|
|
752
774
|
refetch();
|
|
753
775
|
}, [refetch]);
|
|
754
|
-
|
|
776
|
+
|
|
755
777
|
useEffect(() => {
|
|
756
778
|
if (refetchInterval && enabled) {
|
|
757
779
|
const interval = setInterval(refetch, refetchInterval);
|
|
758
780
|
return () => clearInterval(interval);
|
|
759
781
|
}
|
|
760
782
|
}, [refetchInterval, refetch, enabled]);
|
|
761
|
-
|
|
783
|
+
|
|
762
784
|
return { data: data || [], total, loading, error, refetch };
|
|
763
785
|
}
|
|
764
786
|
|
|
765
787
|
export function useOne<T = any>(
|
|
766
788
|
resource: string,
|
|
767
789
|
id: string | number | null | undefined,
|
|
768
|
-
options: UseOneOptions = {}
|
|
790
|
+
options: UseOneOptions = {},
|
|
769
791
|
) {
|
|
770
792
|
const [data, setData] = useState<T | null>(null);
|
|
771
793
|
const [loading, setLoading] = useState<boolean>(true);
|
|
772
794
|
const [error, setError] = useState<DataProviderError | null>(null);
|
|
773
795
|
|
|
774
796
|
const dataProvider = useDataProvider();
|
|
775
|
-
|
|
797
|
+
|
|
776
798
|
const { enabled = true } = options;
|
|
777
|
-
|
|
799
|
+
|
|
778
800
|
const refetch = useCallback(async () => {
|
|
779
801
|
if (!enabled || !id) {
|
|
780
802
|
setLoading(false);
|
|
781
803
|
return;
|
|
782
804
|
}
|
|
783
|
-
|
|
805
|
+
|
|
784
806
|
try {
|
|
785
807
|
setLoading(true);
|
|
786
808
|
setError(null);
|
|
@@ -793,73 +815,81 @@ export function useOne<T = any>(
|
|
|
793
815
|
setLoading(false);
|
|
794
816
|
}
|
|
795
817
|
}, [dataProvider, resource, id, enabled]);
|
|
796
|
-
|
|
818
|
+
|
|
797
819
|
useEffect(() => {
|
|
798
820
|
refetch();
|
|
799
821
|
}, [refetch]);
|
|
800
|
-
|
|
822
|
+
|
|
801
823
|
return { data, loading, error, refetch };
|
|
802
824
|
}
|
|
803
825
|
|
|
804
826
|
export function useCreate<T = any, V = any>(
|
|
805
827
|
resource: string,
|
|
806
828
|
options: UseMutationOptions<T>,
|
|
807
|
-
meta?:AxiosRequestConfig
|
|
829
|
+
meta?: AxiosRequestConfig,
|
|
808
830
|
) {
|
|
809
831
|
const [loading, setLoading] = useState<boolean>(false);
|
|
810
832
|
const [error, setError] = useState<DataProviderError | null>(null);
|
|
811
|
-
|
|
833
|
+
|
|
812
834
|
const dataProvider = useDataProvider();
|
|
813
835
|
|
|
814
836
|
const { onSuccess, onError } = options;
|
|
815
|
-
|
|
816
|
-
const mutate = useCallback(
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
onSuccess
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
onError(errorObj);
|
|
837
|
+
|
|
838
|
+
const mutate = useCallback(
|
|
839
|
+
async (variables: V): Promise<T | undefined> => {
|
|
840
|
+
setLoading(true);
|
|
841
|
+
setError(null);
|
|
842
|
+
|
|
843
|
+
try {
|
|
844
|
+
const result = await dataProvider.create<T, V>(resource, {
|
|
845
|
+
variables,
|
|
846
|
+
meta,
|
|
847
|
+
});
|
|
848
|
+
if (onSuccess) {
|
|
849
|
+
onSuccess(result.data);
|
|
850
|
+
}
|
|
851
|
+
return result.data;
|
|
852
|
+
} catch (err) {
|
|
853
|
+
const errorObj = err as DataProviderError;
|
|
854
|
+
setError(errorObj);
|
|
855
|
+
onError?.(errorObj);
|
|
856
|
+
return;
|
|
857
|
+
} finally {
|
|
858
|
+
setLoading(false);
|
|
834
859
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}, [dataProvider, resource, onSuccess, onError]);
|
|
840
|
-
|
|
860
|
+
},
|
|
861
|
+
[dataProvider, resource, onSuccess, onError],
|
|
862
|
+
);
|
|
863
|
+
|
|
841
864
|
return { mutate, loading, error };
|
|
842
865
|
}
|
|
843
866
|
|
|
844
867
|
export function useUpdate<T = any, V = any>(
|
|
845
868
|
resource: string,
|
|
846
869
|
options: UseMutationOptions<T>,
|
|
847
|
-
meta?: AxiosRequestConfig
|
|
870
|
+
meta?: AxiosRequestConfig,
|
|
848
871
|
) {
|
|
849
872
|
const [loading, setLoading] = useState<boolean>(false);
|
|
850
873
|
const [error, setError] = useState<DataProviderError | null>(null);
|
|
851
874
|
|
|
852
875
|
const dataProvider = useDataProvider();
|
|
853
|
-
|
|
876
|
+
|
|
854
877
|
const { onSuccess, onError } = options;
|
|
855
|
-
|
|
878
|
+
|
|
856
879
|
const mutate = useCallback(
|
|
857
|
-
async (
|
|
880
|
+
async (
|
|
881
|
+
id: string | number,
|
|
882
|
+
variables: Partial<V>,
|
|
883
|
+
): Promise<T | undefined> => {
|
|
858
884
|
setLoading(true);
|
|
859
885
|
setError(null);
|
|
860
|
-
|
|
886
|
+
|
|
861
887
|
try {
|
|
862
|
-
const result = await dataProvider.update<T, V>(resource, {
|
|
888
|
+
const result = await dataProvider.update<T, V>(resource, {
|
|
889
|
+
id,
|
|
890
|
+
variables,
|
|
891
|
+
meta,
|
|
892
|
+
});
|
|
863
893
|
if (onSuccess) {
|
|
864
894
|
onSuccess(result.data);
|
|
865
895
|
}
|
|
@@ -867,37 +897,35 @@ export function useUpdate<T = any, V = any>(
|
|
|
867
897
|
} catch (err) {
|
|
868
898
|
const errorObj = err as DataProviderError;
|
|
869
899
|
setError(errorObj);
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
throw errorObj;
|
|
900
|
+
onError?.(errorObj);
|
|
901
|
+
return;
|
|
874
902
|
} finally {
|
|
875
903
|
setLoading(false);
|
|
876
904
|
}
|
|
877
|
-
},
|
|
878
|
-
[dataProvider, resource, onSuccess, onError]
|
|
905
|
+
},
|
|
906
|
+
[dataProvider, resource, onSuccess, onError],
|
|
879
907
|
);
|
|
880
|
-
|
|
908
|
+
|
|
881
909
|
return { mutate, loading, error };
|
|
882
910
|
}
|
|
883
911
|
|
|
884
912
|
export function useDelete<T = any>(
|
|
885
913
|
resource: string,
|
|
886
914
|
options: UseMutationOptions<T>,
|
|
887
|
-
meta?: AxiosRequestConfig
|
|
915
|
+
meta?: AxiosRequestConfig,
|
|
888
916
|
) {
|
|
889
917
|
const [loading, setLoading] = useState<boolean>(false);
|
|
890
918
|
const [error, setError] = useState<DataProviderError | null>(null);
|
|
891
919
|
|
|
892
920
|
const dataProvider = useDataProvider();
|
|
893
|
-
|
|
921
|
+
|
|
894
922
|
const { onSuccess, onError } = options;
|
|
895
|
-
|
|
923
|
+
|
|
896
924
|
const mutate = useCallback(
|
|
897
|
-
async (id: string | number): Promise<T> => {
|
|
925
|
+
async (id: string | number): Promise<T | undefined> => {
|
|
898
926
|
setLoading(true);
|
|
899
927
|
setError(null);
|
|
900
|
-
|
|
928
|
+
|
|
901
929
|
try {
|
|
902
930
|
const result = await dataProvider.deleteOne<T>(resource, { id, meta });
|
|
903
931
|
if (onSuccess) {
|
|
@@ -907,17 +935,15 @@ export function useDelete<T = any>(
|
|
|
907
935
|
} catch (err) {
|
|
908
936
|
const errorObj = err as DataProviderError;
|
|
909
937
|
setError(errorObj);
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
throw errorObj;
|
|
938
|
+
onError?.(errorObj);
|
|
939
|
+
return;
|
|
914
940
|
} finally {
|
|
915
941
|
setLoading(false);
|
|
916
942
|
}
|
|
917
|
-
},
|
|
918
|
-
[dataProvider, resource, onSuccess, onError]
|
|
943
|
+
},
|
|
944
|
+
[dataProvider, resource, onSuccess, onError],
|
|
919
945
|
);
|
|
920
|
-
|
|
946
|
+
|
|
921
947
|
return { mutate, loading, error };
|
|
922
948
|
}
|
|
923
949
|
|
|
@@ -934,7 +960,7 @@ export function useCustom<T = any>(
|
|
|
934
960
|
const { onSuccess, onError } = options;
|
|
935
961
|
|
|
936
962
|
const mutate = useCallback(
|
|
937
|
-
async (variables?: Payload): Promise<T> => {
|
|
963
|
+
async (variables?: Payload): Promise<T | undefined> => {
|
|
938
964
|
setLoading(true);
|
|
939
965
|
setError(null);
|
|
940
966
|
|
|
@@ -945,7 +971,7 @@ export function useCustom<T = any>(
|
|
|
945
971
|
method: options.method,
|
|
946
972
|
headers: options.headers,
|
|
947
973
|
query: options.query,
|
|
948
|
-
...options
|
|
974
|
+
...options,
|
|
949
975
|
});
|
|
950
976
|
if (onSuccess) {
|
|
951
977
|
onSuccess(result.data);
|
|
@@ -955,15 +981,13 @@ export function useCustom<T = any>(
|
|
|
955
981
|
} catch (err) {
|
|
956
982
|
const errorObj = err as DataProviderError;
|
|
957
983
|
setError(errorObj);
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
throw errorObj;
|
|
984
|
+
onError?.(errorObj);
|
|
985
|
+
return;
|
|
962
986
|
} finally {
|
|
963
987
|
setLoading(false);
|
|
964
988
|
}
|
|
965
989
|
},
|
|
966
|
-
[dataProvider, resource, onSuccess, onError]
|
|
990
|
+
[dataProvider, resource, onSuccess, onError],
|
|
967
991
|
);
|
|
968
992
|
|
|
969
993
|
return { mutate, loading, error, data: customData };
|
|
@@ -986,7 +1010,7 @@ export const useDataProvider = () => {
|
|
|
986
1010
|
const ctx = useContext(DataProviderContext);
|
|
987
1011
|
if (!ctx) {
|
|
988
1012
|
throw new Error(
|
|
989
|
-
"useDataProvider must be used inside <DataProviderContainer />"
|
|
1013
|
+
"useDataProvider must be used inside <DataProviderContainer />",
|
|
990
1014
|
);
|
|
991
1015
|
}
|
|
992
1016
|
return ctx;
|
|
@@ -1004,27 +1028,29 @@ export const useDataProvider = () => {
|
|
|
1004
1028
|
*/
|
|
1005
1029
|
|
|
1006
1030
|
export interface ICreateHttpClientOptions {
|
|
1007
|
-
tokenName?: string
|
|
1031
|
+
tokenName?: string;
|
|
1008
1032
|
tokenStorage?: "local" | "session" | "cookie" | "http-only";
|
|
1009
1033
|
authorizationType?: "Bearer" | "Basic" | string;
|
|
1010
1034
|
withCredentials?: boolean;
|
|
1011
1035
|
}
|
|
1012
1036
|
|
|
1013
|
-
export
|
|
1037
|
+
export interface ICreateHttpClient {
|
|
1014
1038
|
url?: string;
|
|
1015
|
-
options?: ICreateHttpClientOptions
|
|
1039
|
+
options?: ICreateHttpClientOptions;
|
|
1016
1040
|
}
|
|
1017
|
-
export function createHttpClient({
|
|
1041
|
+
export function createHttpClient({
|
|
1042
|
+
url,
|
|
1043
|
+
options = {},
|
|
1044
|
+
}: ICreateHttpClient): AxiosInstance {
|
|
1018
1045
|
const {
|
|
1019
1046
|
tokenName = "token",
|
|
1020
1047
|
tokenStorage = "http-only",
|
|
1021
1048
|
authorizationType = "Bearer",
|
|
1022
1049
|
} = options;
|
|
1023
|
-
|
|
1050
|
+
|
|
1024
1051
|
const withCredentials =
|
|
1025
1052
|
options.withCredentials ?? tokenStorage === "http-only";
|
|
1026
1053
|
|
|
1027
|
-
|
|
1028
1054
|
const axiosInstance = axios.create({
|
|
1029
1055
|
baseURL: url || "https://api.example.com",
|
|
1030
1056
|
withCredentials,
|
|
@@ -1047,8 +1073,8 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
|
|
|
1047
1073
|
break;
|
|
1048
1074
|
|
|
1049
1075
|
case "http-only":
|
|
1050
|
-
// NO READ
|
|
1051
|
-
// HttpOnly cookies are not accessible via JavaScript
|
|
1076
|
+
// NO READ
|
|
1077
|
+
// HttpOnly cookies are not accessible via JavaScript
|
|
1052
1078
|
// Browser will send it automatically
|
|
1053
1079
|
break;
|
|
1054
1080
|
}
|
|
@@ -1056,7 +1082,7 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
|
|
|
1056
1082
|
// Just set token if available in storage
|
|
1057
1083
|
if (token) {
|
|
1058
1084
|
config.headers.Authorization = `${authorizationType} ${token}`;
|
|
1059
|
-
}
|
|
1085
|
+
}
|
|
1060
1086
|
|
|
1061
1087
|
return config;
|
|
1062
1088
|
});
|
|
@@ -1066,7 +1092,6 @@ export function createHttpClient({url, options = {}}:ICreateHttpClient): AxiosIn
|
|
|
1066
1092
|
|
|
1067
1093
|
// ================================ AUTH ================================
|
|
1068
1094
|
|
|
1069
|
-
|
|
1070
1095
|
export interface AuthUser {
|
|
1071
1096
|
id: string | number;
|
|
1072
1097
|
email?: string;
|
|
@@ -1134,7 +1159,7 @@ export const useAuth = () => {
|
|
|
1134
1159
|
export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
1135
1160
|
children,
|
|
1136
1161
|
urls,
|
|
1137
|
-
tokenKey =
|
|
1162
|
+
tokenKey = "token",
|
|
1138
1163
|
keysCleanUpOnLogout = ["token"],
|
|
1139
1164
|
}) => {
|
|
1140
1165
|
const dataProvider = useDataProvider();
|
|
@@ -1148,7 +1173,8 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
|
1148
1173
|
sessionStorage.removeItem(key);
|
|
1149
1174
|
}
|
|
1150
1175
|
|
|
1151
|
-
const login = useCallback(
|
|
1176
|
+
const login = useCallback(
|
|
1177
|
+
async (payload: LoginPayload, type: TypeResponse = "full") => {
|
|
1152
1178
|
try {
|
|
1153
1179
|
const res = await dataProvider.custom<any>({
|
|
1154
1180
|
url: loginUrl,
|
|
@@ -1161,39 +1187,43 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
|
1161
1187
|
}
|
|
1162
1188
|
return res;
|
|
1163
1189
|
} catch (error) {
|
|
1164
|
-
|
|
1190
|
+
if (error instanceof Error) return Promise.reject(error);
|
|
1191
|
+
return Promise.reject(error);
|
|
1165
1192
|
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1193
|
+
},
|
|
1194
|
+
[dataProvider, loginUrl],
|
|
1195
|
+
);
|
|
1168
1196
|
|
|
1169
|
-
const logout = useCallback(
|
|
1197
|
+
const logout = useCallback(
|
|
1198
|
+
async (params: CustomParams, type: TypeResponse = "full") => {
|
|
1199
|
+
try {
|
|
1200
|
+
const res = await dataProvider.custom<any>({
|
|
1201
|
+
url: params.url || logoutUrl,
|
|
1202
|
+
...params,
|
|
1203
|
+
});
|
|
1170
1204
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
...params,
|
|
1175
|
-
});
|
|
1205
|
+
if (type === "simple") {
|
|
1206
|
+
return res.data;
|
|
1207
|
+
}
|
|
1176
1208
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
removeKeys(
|
|
1190
|
-
}
|
|
1191
|
-
} else {
|
|
1192
|
-
removeKeys(keysCleanUpOnLogout);
|
|
1209
|
+
return res;
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
if (error instanceof Error) return Promise.reject(error);
|
|
1212
|
+
return Promise.reject(error);
|
|
1213
|
+
} finally {
|
|
1214
|
+
setUser(null);
|
|
1215
|
+
dataProvider.clearAllCache();
|
|
1216
|
+
if (Array.isArray(keysCleanUpOnLogout)) {
|
|
1217
|
+
keysCleanUpOnLogout.forEach((key) => {
|
|
1218
|
+
removeKeys(key);
|
|
1219
|
+
});
|
|
1220
|
+
} else {
|
|
1221
|
+
removeKeys(keysCleanUpOnLogout);
|
|
1222
|
+
}
|
|
1193
1223
|
}
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1224
|
+
},
|
|
1225
|
+
[dataProvider, logoutUrl],
|
|
1226
|
+
);
|
|
1197
1227
|
|
|
1198
1228
|
const isAuthenticated = () => {
|
|
1199
1229
|
return user !== null;
|
|
@@ -1202,27 +1232,29 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
|
1202
1232
|
return cookiesProvider.get(tokenKey);
|
|
1203
1233
|
}, [tokenKey]);
|
|
1204
1234
|
|
|
1205
|
-
const getMe = useCallback(
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1235
|
+
const getMe = useCallback(
|
|
1236
|
+
async (type?: TypeResponse) => {
|
|
1237
|
+
try {
|
|
1238
|
+
const res = await dataProvider.custom<AuthUser>({
|
|
1239
|
+
url: meUrl,
|
|
1240
|
+
method: "get",
|
|
1241
|
+
});
|
|
1211
1242
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1243
|
+
if (type === "simple") {
|
|
1244
|
+
return res.data;
|
|
1245
|
+
}
|
|
1246
|
+
return res;
|
|
1247
|
+
} catch {
|
|
1248
|
+
return null;
|
|
1214
1249
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
}, [dataProvider, meUrl]);
|
|
1250
|
+
},
|
|
1251
|
+
[dataProvider, meUrl],
|
|
1252
|
+
);
|
|
1220
1253
|
|
|
1221
1254
|
useEffect(() => {
|
|
1222
1255
|
getMe().then((u: any) => u && setUser(u));
|
|
1223
1256
|
}, [getMe]);
|
|
1224
1257
|
|
|
1225
|
-
|
|
1226
1258
|
const value: AuthContextValue = {
|
|
1227
1259
|
user,
|
|
1228
1260
|
setUser,
|
|
@@ -1232,7 +1264,6 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
|
1232
1264
|
logout,
|
|
1233
1265
|
getMe,
|
|
1234
1266
|
};
|
|
1235
|
-
|
|
1236
1267
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
1237
1268
|
};
|
|
1238
1269
|
|
|
@@ -1251,33 +1282,29 @@ export const cookiesProvider = {
|
|
|
1251
1282
|
},
|
|
1252
1283
|
|
|
1253
1284
|
remove: (name: string) => {
|
|
1254
|
-
Cookies.remove(name, {path: "/"});
|
|
1285
|
+
Cookies.remove(name, { path: "/" });
|
|
1255
1286
|
},
|
|
1256
1287
|
|
|
1257
|
-
|
|
1258
1288
|
exists: (name: string): boolean => {
|
|
1259
1289
|
return Cookies.get(name) !== undefined;
|
|
1260
1290
|
},
|
|
1261
1291
|
};
|
|
1262
1292
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
1293
|
// =================== Example ===================
|
|
1266
1294
|
|
|
1267
1295
|
// create httpClient - (can create multiple httpClients for different apis)
|
|
1268
1296
|
|
|
1269
1297
|
// const TOKEN = "token";
|
|
1270
1298
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1299
|
+
// const httpClient = createHttpClient({
|
|
1300
|
+
// url: `${process.env.NEXT_PUBLIC_API_URL}`,
|
|
1301
|
+
// options: {
|
|
1302
|
+
// authorizationType: "Bearer",
|
|
1303
|
+
// tokenName: TOKEN,
|
|
1304
|
+
// tokenStorage: "cookie",
|
|
1305
|
+
// withCredentials: true, --- optionals (default to true if tokenStorage is "http-only")
|
|
1306
|
+
// },
|
|
1307
|
+
// });
|
|
1281
1308
|
|
|
1282
1309
|
// create dataProvider
|
|
1283
1310
|
|
|
@@ -1293,16 +1320,15 @@ export const cookiesProvider = {
|
|
|
1293
1320
|
|
|
1294
1321
|
// wrapped all into:
|
|
1295
1322
|
// <DataProvider dataProvider={dataProvider}>
|
|
1296
|
-
// <AuthProvider
|
|
1297
|
-
// urls={urls} --> api_login
|
|
1298
|
-
// tokenKey={TOKEN}
|
|
1323
|
+
// <AuthProvider
|
|
1324
|
+
// urls={urls} --> api_login
|
|
1325
|
+
// tokenKey={TOKEN}
|
|
1299
1326
|
// keysCleanUpOnLogout={keysRemoveOnLogout} --> optional (default to ["token"]) - additional keys to clean up on logout
|
|
1300
1327
|
// >
|
|
1301
1328
|
// <App />
|
|
1302
1329
|
// </AuthProvider>
|
|
1303
1330
|
// </DataProvider>
|
|
1304
1331
|
|
|
1305
|
-
|
|
1306
1332
|
// use hooks to auth
|
|
1307
1333
|
|
|
1308
1334
|
// const { login, logout, refresh } = useAuth();
|
|
@@ -1338,4 +1364,4 @@ export const cookiesProvider = {
|
|
|
1338
1364
|
// url: '/route_name or api_url/route/...',
|
|
1339
1365
|
// method: 'post',
|
|
1340
1366
|
// payload: {},
|
|
1341
|
-
// });
|
|
1367
|
+
// });
|