ente/web/packages/shared/network/HTTPService.ts

264 lines
7.3 KiB
TypeScript
Raw Normal View History

import log from "@/next/log";
2024-05-24 12:50:40 +00:00
import axios, { type AxiosRequestConfig, type AxiosResponse } from "axios";
import { ApiError, CustomError, isApiErrorResponse } from "../error";
2023-11-02 03:36:31 +00:00
interface IHTTPHeaders {
[headerKey: string]: any;
}
interface IQueryPrams {
[paramName: string]: any;
}
/**
* Service to manage all HTTP calls.
*/
class HTTPService {
constructor() {
axios.interceptors.response.use(
(response) => Promise.resolve(response),
(error) => {
2023-11-02 15:22:31 +00:00
const config = error.config as AxiosRequestConfig;
2023-11-02 03:36:31 +00:00
if (error.response) {
const response = error.response as AxiosResponse;
let apiError: ApiError;
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
if (isApiErrorResponse(response.data)) {
const responseData = response.data;
log.error(
`HTTP Service Error - ${JSON.stringify({
url: config?.url,
method: config?.method,
xRequestId: response.headers["x-request-id"],
httpStatus: response.status,
errMessage: responseData.message,
errCode: responseData.code,
})}`,
error,
);
2023-11-02 03:36:31 +00:00
apiError = new ApiError(
responseData.message,
responseData.code,
response.status,
2023-11-02 03:36:31 +00:00
);
} else {
if (response.status >= 400 && response.status < 500) {
apiError = new ApiError(
CustomError.CLIENT_ERROR,
"",
response.status,
2023-11-02 03:36:31 +00:00
);
} else {
apiError = new ApiError(
CustomError.ServerError,
"",
response.status,
2023-11-02 03:36:31 +00:00
);
}
}
log.error(
`HTTP Service Error - ${JSON.stringify({
url: config.url,
method: config.method,
cfRay: response.headers["cf-ray"],
xRequestId: response.headers["x-request-id"],
httpStatus: response.status,
})}`,
apiError,
);
2023-11-02 03:36:31 +00:00
throw apiError;
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
log.info(
`request failed - no response (${config.method} ${config.url}`,
2023-11-02 15:24:20 +00:00
);
2023-11-02 03:36:31 +00:00
return Promise.reject(error);
} else {
// Something happened in setting up the request that
// triggered an Error
log.info(
`request failed - axios error (${config.method} ${config.url}`,
2023-11-02 15:24:20 +00:00
);
2023-11-02 03:36:31 +00:00
return Promise.reject(error);
}
},
2023-11-02 03:36:31 +00:00
);
}
/**
* header object to be append to all api calls.
*/
private headers: IHTTPHeaders = {
"content-type": "application/json",
2023-11-02 03:36:31 +00:00
};
/**
* Sets the headers to the given object.
*/
public setHeaders(headers: IHTTPHeaders) {
this.headers = {
...this.headers,
...headers,
};
}
/**
* Adds a header to list of headers.
*/
public appendHeader(key: string, value: string) {
this.headers = {
...this.headers,
[key]: value,
};
}
/**
* Removes the given header.
*/
public removeHeader(key: string) {
this.headers[key] = undefined;
}
/**
* Returns axios interceptors.
*/
public getInterceptors() {
return axios.interceptors;
}
/**
* Generic HTTP request.
* This is done so that developer can use any functionality
* provided by axios. Here, only the set headers are spread
* over what was sent in config.
*/
public async request(config: AxiosRequestConfig, customConfig?: any) {
config.headers = {
...this.headers,
...config.headers,
};
if (customConfig?.cancel) {
config.cancelToken = new axios.CancelToken(
(c) => (customConfig.cancel.exec = c),
2023-11-02 03:36:31 +00:00
);
}
return await axios({ ...config, ...customConfig });
}
/**
* Get request.
*/
public get(
url: string,
params?: IQueryPrams,
headers?: IHTTPHeaders,
customConfig?: any,
2023-11-02 03:36:31 +00:00
) {
return this.request(
{
headers,
method: "GET",
2023-11-02 03:36:31 +00:00
params,
url,
},
customConfig,
2023-11-02 03:36:31 +00:00
);
}
/**
* Post request
*/
public post(
url: string,
data?: any,
params?: IQueryPrams,
headers?: IHTTPHeaders,
customConfig?: any,
2023-11-02 03:36:31 +00:00
) {
return this.request(
{
data,
headers,
method: "POST",
2023-11-02 03:36:31 +00:00
params,
url,
},
customConfig,
2023-12-24 22:01:03 +00:00
);
}
/**
* Patch request
*/
public patch(
url: string,
data?: any,
params?: IQueryPrams,
headers?: IHTTPHeaders,
customConfig?: any,
2023-12-24 22:01:03 +00:00
) {
return this.request(
{
data,
headers,
method: "PATCH",
2023-12-24 22:01:03 +00:00
params,
url,
},
customConfig,
2023-11-02 03:36:31 +00:00
);
}
/**
* Put request
*/
public put(
url: string,
data: any,
params?: IQueryPrams,
headers?: IHTTPHeaders,
customConfig?: any,
2023-11-02 03:36:31 +00:00
) {
return this.request(
{
data,
headers,
method: "PUT",
2023-11-02 03:36:31 +00:00
params,
url,
},
customConfig,
2023-11-02 03:36:31 +00:00
);
}
/**
* Delete request
*/
public delete(
url: string,
data: any,
params?: IQueryPrams,
headers?: IHTTPHeaders,
customConfig?: any,
2023-11-02 03:36:31 +00:00
) {
return this.request(
{
data,
headers,
method: "DELETE",
2023-11-02 03:36:31 +00:00
params,
url,
},
customConfig,
2023-11-02 03:36:31 +00:00
);
}
}
// Creates a Singleton Service.
// This will help me maintain common headers / functionality
// at a central place.
export default new HTTPService();