1
1
import { Models } from './models';
2
- import { Service } from './service';
3
2
4
3
/**
5
4
* Payload type representing a key-value pair with string keys and any values.
@@ -48,7 +47,7 @@ type RealtimeRequest = {
48
47
/**
49
48
* Realtime event response structure with generic payload type.
50
49
*/
51
- export type RealtimeResponseEvent<T extends unknown > = {
50
+ type RealtimeResponseEvent<T extends unknown > = {
52
51
/**
53
52
* List of event names associated with the response.
54
53
*/
@@ -215,7 +214,7 @@ type Realtime = {
215
214
/**
216
215
* Type representing upload progress information.
217
216
*/
218
- export type UploadProgress = {
217
+ type UploadProgress = {
219
218
/**
220
219
* Identifier for the upload progress.
221
220
*/
@@ -284,17 +283,18 @@ class {{spec.title | caseUcfirst}}Exception extends Error {
284
283
* Client that handles requests to {{spec .title | caseUcfirst }}
285
284
*/
286
285
class Client {
286
+ static CHUNK_SIZE = 1024 * 1024 * 5;
287
+
287
288
/**
288
289
* Holds configuration such as project.
289
290
*/
290
291
config = {
291
292
endpoint: '{{ spec .endpoint }}',
292
293
endpointRealtime: '',
293
- {% for header in spec .global .headers %}
294
+ {%~ for header in spec .global .headers %}
294
295
{{ header .key | caseLower }}: '',
295
- {% endfor %}
296
+ {%~ endfor %}
296
297
};
297
-
298
298
/**
299
299
* Custom headers for API requests.
300
300
*/
@@ -303,9 +303,9 @@ class Client {
303
303
'x-sdk-platform': '{{ sdk .platform }}',
304
304
'x-sdk-language': '{{ language .name | caseLower }}',
305
305
'x-sdk-version': '{{ sdk .version }}',
306
- {% for key ,header in spec .global .defaultHeaders %}
306
+ {%~ for key ,header in spec .global .defaultHeaders %}
307
307
'{{key }}': '{{header }}',
308
- {% endfor %}
308
+ {%~ endfor %}
309
309
};
310
310
311
311
/**
@@ -337,14 +337,14 @@ class Client {
337
337
return this;
338
338
}
339
339
340
- {% for header in spec .global .headers %}
340
+ {%~ for header in spec .global .headers %}
341
341
/**
342
342
* Set {{header .key | caseUcfirst }}
343
343
*
344
- {% if header .description %}
345
- {{header .description | comment2 }}
344
+ {%~ if header .description %}
345
+ * {{header .description }}
346
346
*
347
- {% endif %}
347
+ {%~ endif %}
348
348
* @param value string
349
349
*
350
350
* @return {this}
@@ -354,8 +354,7 @@ class Client {
354
354
this.config.{{ header .key | caseLower }} = value;
355
355
return this;
356
356
}
357
-
358
- {% endfor %}
357
+ {%~ endfor %}
359
358
360
359
private realtime: Realtime = {
361
360
socket: undefined,
@@ -540,40 +539,18 @@ class Client {
540
539
}
541
540
}
542
541
543
- /**
544
- * Call API endpoint with the specified method, URL, headers, and parameters.
545
- *
546
- * @param {string} method - HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE').
547
- * @param {URL} url - The URL of the API endpoint.
548
- * @param {Headers} headers - Custom headers for the API request.
549
- * @param {Payload} params - Request parameters.
550
- * @returns {Promise<any >} - A promise that resolves with the response data.
551
- *
552
- * @typedef {Object} Payload - Request payload data.
553
- * @property {string} key - The key.
554
- * @property {string} value - The value.
555
- */
556
- async call(method: string, url: URL, headers: Headers = {}, params: Payload = {}): Promise<any > {
542
+ prepareRequest(method: string, url: URL, headers: Headers = {}, params: Payload = {}): { uri: string, options: RequestInit } {
557
543
method = method.toUpperCase();
558
544
559
-
560
545
headers = Object.assign({}, this.headers, headers);
561
546
562
547
let options: RequestInit = {
563
548
method,
564
549
headers,
565
- credentials: 'include'
566
550
};
567
551
568
- if (typeof window !== 'undefined' && window.localStorage) {
569
- const cookieFallback = window.localStorage.getItem('cookieFallback');
570
- if (cookieFallback) {
571
- headers['X-Fallback-Cookies'] = cookieFallback;
572
- }
573
- }
574
-
575
552
if (method === 'GET') {
576
- for (const [key, value] of Object.entries(Service .flatten(params))) {
553
+ for (const [key, value] of Object.entries(Client .flatten(params))) {
577
554
url.searchParams.append(key, value);
578
555
}
579
556
} else {
@@ -583,15 +560,17 @@ class Client {
583
560
break;
584
561
585
562
case 'multipart/form-data':
586
- let formData = new FormData();
587
-
588
- for (const key in params) {
589
- if (Array.isArray(params[key])) {
590
- params[key].forEach((value: any) => {
591
- formData.append(key + '[]', value);
592
- })
563
+ const formData = new FormData();
564
+
565
+ for (const [key, value] of Object.entries(params)) {
566
+ if (value instanceof File) {
567
+ formData.append(key, value, value.name);
568
+ } else if (Array.isArray(value)) {
569
+ for (const nestedValue of value) {
570
+ formData.append(`${key}[]`, nestedValue);
571
+ }
593
572
} else {
594
- formData.append(key, params[key] );
573
+ formData.append(key, value );
595
574
}
596
575
}
597
576
@@ -601,45 +580,121 @@ class Client {
601
580
}
602
581
}
603
582
604
- try {
605
- let data = null;
606
- const response = await fetch(url.toString(), options);
583
+ return { uri: url.toString(), options };
584
+ }
585
+
586
+ async chunkedUpload(method: string, url: URL, headers: Headers = {}, originalPayload: Payload = {}, onProgress: (progress: UploadProgress) => void) {
587
+ const file = Object.values(originalPayload).find((value) => value instanceof File);
588
+
589
+ if (file.size < = Client.CHUNK_SIZE) {
590
+ return await this.call(method, url, headers, originalPayload);
591
+ }
592
+
593
+ let start = 0;
594
+ let response = null;
607
595
608
- const warnings = response.headers.get('x-{{ spec .title | lower }}-warning');
609
- if (warnings) {
610
- warnings.split(';').forEach((warning: string) => console.warn('Warning: ' + warning));
596
+ while (start < file.size) {
597
+ let end = start + Client.CHUNK_SIZE; // Prepare end for the next chunk
598
+ if (end >= file.size) {
599
+ end = file.size; // Adjust for the last chunk to include the last byte
611
600
}
612
601
613
- if (response.headers.get('content-type')?.includes('application/json')) {
614
- data = await response.json();
615
- } else {
616
- data = {
617
- message: await response.text()
618
- };
602
+ headers['content-range'] = `bytes ${start}-${end-1}/${file.size}`;
603
+ const chunk = file.slice(start, end);
604
+
605
+ let payload = { ...originalPayload, file: new File([chunk], file.name)};
606
+
607
+ response = await this.call(method, url, headers, payload);
608
+
609
+ if (onProgress && typeof onProgress === 'function') {
610
+ onProgress({
611
+ $id: response.$id,
612
+ progress: Math.round((end / file.size) * 100),
613
+ sizeUploaded: end,
614
+ chunksTotal: Math.ceil(file.size / Client.CHUNK_SIZE),
615
+ chunksUploaded: Math.ceil(end / Client.CHUNK_SIZE)
616
+ });
619
617
}
620
618
621
- if (400 < = response.status ) {
622
- throw new {{spec .title | caseUcfirst }}Exception(data?.message, response.status, data?.type, data) ;
619
+ if (response && response.$id ) {
620
+ headers['x- {{spec .title | caseLower }}-id'] = response.$id ;
623
621
}
624
622
625
- const cookieFallback = response.headers.get('X-Fallback-Cookies');
623
+ start = end;
624
+ }
626
625
627
- if (typeof window !== 'undefined' && window.localStorage && cookieFallback) {
628
- window.console.warn('{{spec .title | caseUcfirst }} is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');
629
- window.localStorage.setItem('cookieFallback', cookieFallback);
630
- }
626
+ return response;
627
+ }
628
+
629
+ async redirect(method: string, url: URL, headers: Headers = {}, params: Payload = {}): Promise<string > {
630
+ const { uri, options } = this.prepareRequest(method, url, headers, params);
631
+
632
+ const response = await fetch(uri, {
633
+ ...options,
634
+ redirect: 'manual'
635
+ });
636
+
637
+ if (response.status !== 301 && response.status !== 302) {
638
+ throw new {{spec .title | caseUcfirst }}Exception('Invalid redirect', response.status);
639
+ }
640
+
641
+ return response.headers.get('location') || '';
642
+ }
631
643
632
- return data;
633
- } catch (e) {
634
- if (e instanceof {{spec .title | caseUcfirst }}Exception) {
635
- throw e;
644
+ async call(method: string, url: URL, headers: Headers = {}, params: Payload = {}, responseType = 'json'): Promise<any > {
645
+ const { uri, options } = this.prepareRequest(method, url, headers, params);
646
+
647
+ let data: any = null;
648
+
649
+ const response = await fetch(uri, options);
650
+
651
+ const warnings = response.headers.get('x-{{ spec .title | lower }}-warning');
652
+ if (warnings) {
653
+ warnings.split(';').forEach((warning: string) => console.warn('Warning: ' + warning));
654
+ }
655
+
656
+ if (response.headers.get('content-type')?.includes('application/json')) {
657
+ data = await response.json();
658
+ } else if (responseType === 'arrayBuffer') {
659
+ data = await response.arrayBuffer();
660
+ } else {
661
+ data = {
662
+ message: await response.text()
663
+ };
664
+ }
665
+
666
+ if (400 < = response.status) {
667
+ throw new {{spec .title | caseUcfirst }}Exception(data?.message, response.status, data?.type, data);
668
+ }
669
+
670
+ const cookieFallback = response.headers.get('X-Fallback-Cookies');
671
+
672
+ if (typeof window !== 'undefined' && window.localStorage && cookieFallback) {
673
+ window.console.warn('{{spec .title | caseUcfirst }} is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');
674
+ window.localStorage.setItem('cookieFallback', cookieFallback);
675
+ }
676
+
677
+ return data;
678
+ }
679
+
680
+ static flatten(data: Payload, prefix = ''): Payload {
681
+ let output: Payload = {};
682
+
683
+ for (const [key, value] of Object.entries(data)) {
684
+ let finalKey = prefix ? prefix + '[' + key +']' : key;
685
+ if (Array.isArray(value)) {
686
+ output = { ...output, ...Client.flatten(value, finalKey) };
687
+ } else {
688
+ output[finalKey] = value;
636
689
}
637
- throw new {{spec .title | caseUcfirst }}Exception((<Error >e).message);
638
690
}
691
+
692
+ return output;
639
693
}
640
694
}
641
695
642
696
export { Client, {{spec .title | caseUcfirst }}Exception };
643
697
export { Query } from './query';
644
- export type { Models, Payload };
698
+ export type { Models, Payload, UploadProgress };
699
+ export type { RealtimeResponseEvent };
645
700
export type { QueryTypes, QueryTypesList } from './query';
0 commit comments