🐛 Improve Axios compatibility (#2262)

* Improved the error object returned by axios to make it more compatible with request

* Fixed multipart/form-data payload creation

* 🐛 Remove issue with circular references

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Omar Ajoue 2021-09-30 21:10:56 +02:00 committed by GitHub
parent 04a043616e
commit 4a3d3cd331
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -91,6 +91,33 @@ const requestPromiseWithDefaults = requestPromise.defaults({
timeout: 300000, // 5 minutes timeout: 300000, // 5 minutes
}); });
const pushFormDataValue = (form: FormData, key: string, value: any) => {
if (value?.hasOwnProperty('value') && value.hasOwnProperty('options')) {
// @ts-ignore
form.append(key, value.value, value.options);
} else {
form.append(key, value);
}
};
const createFormDataObject = (data: object) => {
const formData = new FormData();
const keys = Object.keys(data);
keys.forEach((key) => {
// @ts-ignore
const formField = data[key];
if (formField instanceof Array) {
formField.forEach((item) => {
pushFormDataValue(formData, key, item);
});
} else {
pushFormDataValue(formData, key, formField);
}
});
return formData;
};
async function parseRequestObject(requestObject: IDataObject) { async function parseRequestObject(requestObject: IDataObject) {
// This function is a temporary implementation // This function is a temporary implementation
// That translates all http requests done via // That translates all http requests done via
@ -139,28 +166,12 @@ async function parseRequestObject(requestObject: IDataObject) {
if (requestObject.formData !== undefined && requestObject.formData instanceof FormData) { if (requestObject.formData !== undefined && requestObject.formData instanceof FormData) {
axiosConfig.data = requestObject.formData; axiosConfig.data = requestObject.formData;
} else { } else {
const allData = Object.assign(requestObject.body || {}, requestObject.formData || {}); const allData = {
...(requestObject.body as object | undefined),
...(requestObject.formData as object | undefined),
};
const objectKeys = Object.keys(allData); axiosConfig.data = createFormDataObject(allData);
if (objectKeys.length > 0) {
// Should be a standard object. We must convert to formdata
const form = new FormData();
objectKeys.forEach((key) => {
const formField = (allData as IDataObject)[key] as IDataObject;
if (formField.hasOwnProperty('value') && formField.value instanceof Buffer) {
let filename;
// @ts-ignore
if (!!formField.options && formField.options.filename !== undefined) {
filename = (formField.options as IDataObject).filename as string;
}
form.append(key, formField.value, filename);
} else {
form.append(key, formField);
}
});
axiosConfig.data = form;
}
} }
// replace the existing header with a new one that // replace the existing header with a new one that
// contains the boundary property. // contains the boundary property.
@ -197,26 +208,7 @@ async function parseRequestObject(requestObject: IDataObject) {
if (requestObject.formData instanceof FormData) { if (requestObject.formData instanceof FormData) {
axiosConfig.data = requestObject.formData; axiosConfig.data = requestObject.formData;
} else { } else {
const objectKeys = Object.keys(requestObject.formData as object); axiosConfig.data = createFormDataObject(requestObject.formData as object);
if (objectKeys.length > 0) {
// Should be a standard object. We must convert to formdata
const form = new FormData();
objectKeys.forEach((key) => {
const formField = (requestObject.formData as IDataObject)[key] as IDataObject;
if (formField.hasOwnProperty('value') && formField.value instanceof Buffer) {
let filename;
// @ts-ignore
if (!!formField.options && formField.options.filename !== undefined) {
filename = (formField.options as IDataObject).filename as string;
}
form.append(key, formField.value, filename);
} else {
form.append(key, formField);
}
});
axiosConfig.data = form;
}
} }
// Mix in headers as FormData creates the boundary. // Mix in headers as FormData creates the boundary.
const headers = axiosConfig.data.getHeaders(); const headers = axiosConfig.data.getHeaders();
@ -415,10 +407,26 @@ async function proxyRequestToAxios(
} }
}) })
.catch((error) => { .catch((error) => {
// The error-data was made available with request library via "error" but now on Logger.debug('Request proxied to Axios failed', { error });
// axios via "response.data" so copy information over to keep it compatible // Axios hydrates the original error with more data. We extract them.
error.error = error.response.data; // https://github.com/axios/axios/blob/master/lib/core/enhanceError.js
error.statusCode = error.response.status; // Note: `code` is ignored as it's an expected part of the errorData.
const { request, response, isAxiosError, toJSON, config, ...errorData } = error;
error.cause = errorData;
error.error = error.response?.data || errorData;
error.statusCode = error.response?.status;
error.options = config;
// Remove not needed data and so also remove circular references
error.request = undefined;
error.config = undefined;
error.options.adapter = undefined;
error.options.httpsAgent = undefined;
error.options.paramsSerializer = undefined;
error.options.transformRequest = undefined;
error.options.transformResponse = undefined;
error.options.validateStatus = undefined;
reject(error); reject(error);
}); });
}); });