Skip to content

Commit e11e356

Browse files
committed
exposed xhr upload object, response headers and body
1 parent 91ce0a2 commit e11e356

File tree

7 files changed

+164
-59
lines changed

7 files changed

+164
-59
lines changed

src/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ class ImageKit {
8484
* @param uploadOptions
8585
*/
8686
upload(uploadOptions: UploadOptions, options?: Partial<ImageKitOptions>): Promise<UploadResponse>
87-
upload(uploadOptions: UploadOptions, callback: (err: Error | null, response: UploadResponse | null) => void, options?: Partial<ImageKitOptions>): void;
88-
upload(uploadOptions: UploadOptions, callbackOrOptions?: ((err: Error | null, response: UploadResponse | null) => void) | Partial<ImageKitOptions>, options?: Partial<ImageKitOptions>): void | Promise<UploadResponse> {
87+
upload(uploadOptions: UploadOptions, callback: (err: Error | null, response: UploadResponse | null) => void, options?: Partial<ImageKitOptions>): XMLHttpRequest;
88+
upload(uploadOptions: UploadOptions, callbackOrOptions?: ((err: Error | null, response: UploadResponse | null) => void) | Partial<ImageKitOptions>, options?: Partial<ImageKitOptions>): XMLHttpRequest | Promise<UploadResponse> {
8989
let callback;
9090
if (typeof callbackOrOptions === 'function') {
9191
callback = callbackOrOptions;
@@ -96,7 +96,13 @@ class ImageKit {
9696
...this.options,
9797
...options,
9898
};
99-
return promisify<UploadResponse>(this, upload)(uploadOptions, mergedOptions, callback);
99+
const xhr = new XMLHttpRequest();
100+
const promise = promisify<UploadResponse>(this, upload)(xhr, uploadOptions, mergedOptions, callback);
101+
if (typeof promise === "object" && typeof promise.then === "function") {
102+
return promise
103+
} else {
104+
return xhr;
105+
}
100106
}
101107
}
102108

src/interfaces/IKResponse.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
interface ResponseMetadata {
2+
statusCode: number;
3+
headers: Record<string, string | number | boolean>;
4+
}
5+
6+
type IKResponse<T> = T extends Error
7+
? T & { $ResponseMetadata?: ResponseMetadata }
8+
: T & { $ResponseMetadata: ResponseMetadata };
9+
10+
export default IKResponse;

src/interfaces/UploadOptions.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export interface UploadOptions {
8484
*/
8585
overwriteAITags?: boolean
8686
/*
87-
* Default is true. If the request does not have tags , overwriteTags is set to true and a file already exists at the exact location, exiting tags will be removed.
87+
* Default is true. If the request does not have tags , overwriteTags is set to true and a file already exists at the exact location, existing tags will be removed.
8888
* In case the request body has tags, setting overwriteTags to false has no effect and request's tags are set on the asset.
8989
*/
9090
overwriteTags?: boolean
@@ -93,4 +93,9 @@ export interface UploadOptions {
9393
* In case the request body has customMetadata, setting overwriteCustomMetadata to false has no effect and request's customMetadata is set on the asset.
9494
*/
9595
overwriteCustomMetadata?: boolean
96+
/*
97+
* Stringified JSON key-value data to be associated with the asset. Checkout overwriteCustomMetadata parameter to understand default behaviour.
98+
* Before setting any custom metadata on an asset you have to create the field using custom metadata fields API.
99+
*/
100+
customMetadata?: Record<string, string | number | boolean>
96101
}

src/interfaces/UploadResponse.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,8 @@ export interface UploadResponse {
159159
* Field object which will contain the status of each extension at the time of completion of the update/upload request.
160160
*/
161161
extensionStatus?: { [key: string]: string }
162+
/*
163+
* Metadata for the response containing headers and status
164+
*/
165+
$ResponseMetadata?: object
162166
}

src/upload/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { request } from "../utils/request";
44
import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces";
55

66
export const upload = (
7+
xhr: XMLHttpRequest,
78
uploadOptions: UploadOptions,
89
options: ImageKitOptions,
910
callback?: (err: Error | null, response: UploadResponse | null) => void,
@@ -57,5 +58,5 @@ export const upload = (
5758

5859
formData.append("publicKey", options.publicKey);
5960

60-
request(formData, { ...options, authenticationEndpoint: options.authenticationEndpoint }, callback);
61+
request(xhr, formData, { ...options, authenticationEndpoint: options.authenticationEndpoint }, callback);
6162
};

src/utils/request.ts

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,73 @@
11
import respond from "../utils/respond";
22
import errorMessages from "../constants/errorMessages"
33
import { ImageKitOptions, UploadResponse } from "../interfaces";
4+
import IKResponse from "../interfaces/IKResponse";
45

56
interface SignatureResponse {
67
signature: string
78
expire: number
89
token: string
910
}
1011

11-
export const request = (formData: FormData, options: ImageKitOptions & { authenticationEndpoint: string }, callback?: (err: Error | null, response: UploadResponse | null) => void) => {
12-
generateSignatureToken(options, (err, signaturObj) => {
12+
function getResponseHeaderMap(xhr: XMLHttpRequest) {
13+
const headers: Record<string, string | number | boolean> = {};
14+
xhr.getAllResponseHeaders()
15+
.trim()
16+
.split(/[\r\n]+/)
17+
.map(value => value.split(/: /))
18+
.forEach(keyValue => {
19+
headers[keyValue[0].trim()] = keyValue[1].trim();
20+
});
21+
return headers;
22+
}
23+
24+
const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest):IKResponse<UploadResponse> => {
25+
let response = { ...body };
26+
const responseMetadata = {
27+
statusCode: xhr.status,
28+
headers: getResponseHeaderMap(xhr)
29+
}
30+
Object.defineProperty(response, "$ResponseMetadata", {
31+
value: responseMetadata,
32+
enumerable: false,
33+
writable: false
34+
});
35+
return response as IKResponse<UploadResponse>;
36+
}
37+
38+
export const request = (uploadFileXHR: XMLHttpRequest,formData: FormData, options: ImageKitOptions & { authenticationEndpoint: string }, callback?: (err: Error | null, response: UploadResponse | null) => void) => {
39+
var signatureXHR = new XMLHttpRequest();
40+
generateSignatureToken(signatureXHR, options, (err, signaturObj) => {
1341
if (err) {
1442
return respond(true, err, callback)
1543
} else {
1644
formData.append("signature", signaturObj?.signature || "");
1745
formData.append("expire", String(signaturObj?.expire || 0));
1846
formData.append("token", signaturObj?.token || "");
1947

20-
uploadFile(formData, (err, responseSucessText) => {
48+
uploadFile(uploadFileXHR, formData, (err, responseSucessText) => {
2149
if (err) {
2250
return respond(true, err, callback)
2351
}
2452
return respond(false, responseSucessText!, callback)
2553
});
2654
}
2755
});
56+
return uploadFileXHR;
2857
}
2958

30-
export const generateSignatureToken = (options: ImageKitOptions & { authenticationEndpoint: string }, callback: (err: Error | null, response: SignatureResponse | null) => void) => {
31-
var xhr = new XMLHttpRequest();
59+
export const generateSignatureToken = (xhr:XMLHttpRequest, options: ImageKitOptions & { authenticationEndpoint: string }, callback: (err: Error | null, response: SignatureResponse | null) => void) => {
3260
xhr.timeout = 60000;
3361
xhr.open('GET', options.authenticationEndpoint);
3462
xhr.ontimeout = function (e) {
35-
respond(true, errorMessages.AUTH_ENDPOINT_TIMEOUT, callback);
63+
var body = errorMessages.AUTH_ENDPOINT_TIMEOUT;
64+
var result = addResponseHeadersAndBody(body, xhr);
65+
respond(true, result, callback);
3666
};
3767
xhr.onerror = function() {
38-
respond(true, errorMessages.AUTH_ENDPOINT_NETWORK_ERROR, callback);
68+
var body = errorMessages.AUTH_ENDPOINT_NETWORK_ERROR;
69+
var result = addResponseHeadersAndBody(body, xhr);
70+
respond(true, result, callback);
3971
}
4072
xhr.onload = function () {
4173
if (xhr.status === 200) {
@@ -46,13 +78,15 @@ export const generateSignatureToken = (options: ImageKitOptions & { authenticati
4678
expire: body.expire,
4779
token: body.token
4880
}
49-
respond(false, obj, callback)
81+
var result = addResponseHeadersAndBody(obj, xhr);
82+
respond(false, result, callback);
5083
} catch (ex) {
5184
respond(true, ex, callback)
5285
}
5386
} else {
5487
try {
5588
var error = JSON.parse(xhr.responseText);
89+
var result = addResponseHeadersAndBody(error, xhr);
5690
respond(true, error, callback);
5791
} catch (ex) {
5892
respond(true, ex, callback);
@@ -63,27 +97,30 @@ export const generateSignatureToken = (options: ImageKitOptions & { authenticati
6397
return;
6498
}
6599

66-
export const uploadFile = (formData: FormData, callback: (err: Error | null, response: UploadResponse | null) => void) => {
67-
var uploadFileXHR = new XMLHttpRequest();
100+
export const uploadFile = (uploadFileXHR:XMLHttpRequest, formData: FormData, callback: (err: Error | IKResponse<UploadResponse> | null, response: UploadResponse | null) => void) => {
68101
uploadFileXHR.open('POST', 'https://upload.imagekit.io/api/v1/files/upload');
69102
uploadFileXHR.onerror = function() {
70-
respond(true, errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR, callback);
103+
var body = errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR;
104+
var result = addResponseHeadersAndBody(body, uploadFileXHR);
105+
respond(true, result, callback);
71106
return;
72107
}
73108
uploadFileXHR.onload = function () {
74109
if (uploadFileXHR.status === 200) {
75-
var uploadResponse = JSON.parse(uploadFileXHR.responseText);
110+
var body = JSON.parse(uploadFileXHR.responseText);
111+
var uploadResponse = addResponseHeadersAndBody(body, uploadFileXHR);
76112
callback(null, uploadResponse);
77113
}
78114
else if (uploadFileXHR.status !== 200) {
79115
try {
80-
callback(JSON.parse(uploadFileXHR.responseText), null);
116+
var body = JSON.parse(uploadFileXHR.responseText);
117+
var uploadResponse = addResponseHeadersAndBody(body, uploadFileXHR);
118+
callback(uploadResponse, null);
81119
} catch (ex : any) {
82120
callback(ex, null);
83121
}
84122
}
85123
};
86124
uploadFileXHR.send(formData);
87-
return
88125
}
89126

0 commit comments

Comments
 (0)