ApiCaller.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import axios, { AxiosResponse, AxiosRequestConfig } from "axios";
  15. import cookie from "js-cookie";
  16. import cacher from "./Cacher";
  17. import logger from "./ApiLogger";
  18. import ApiCallerHandlers from "./ApiCallerHandlers";
  19. type Cancelable = {
  20. requestId: string;
  21. cancel: () => void;
  22. };
  23. export type RequestOptions = {
  24. url: string;
  25. method?: AxiosRequestConfig["method"];
  26. cancelId?: string;
  27. headers?: { [prop: string]: string };
  28. data?: any;
  29. responseType?:
  30. | "arraybuffer"
  31. | "blob"
  32. | "document"
  33. | "json"
  34. | "text"
  35. | "stream";
  36. quietError?: boolean | null;
  37. skipLog?: boolean | null;
  38. cache?: boolean | null;
  39. cacheFor?: number | null;
  40. timeout?: number;
  41. };
  42. let cancelables: Cancelable[] = [];
  43. const CancelToken = axios.CancelToken;
  44. const addCancelable = (cancelable: Cancelable) => {
  45. cancelables.unshift(cancelable);
  46. if (cancelables.length > 100) {
  47. cancelables.pop();
  48. }
  49. };
  50. class ApiCaller {
  51. constructor() {
  52. axios.defaults.headers.common["Content-Type"] = "application/json";
  53. }
  54. get projectId(): string {
  55. return cookie.get("projectId") || "undefined";
  56. }
  57. removeFromCache(url: string) {
  58. cacher.remove(url);
  59. }
  60. cancelRequests(cancelRequestId: string) {
  61. const filteredCancelables = cancelables.filter(
  62. r => r.requestId === cancelRequestId
  63. );
  64. filteredCancelables.forEach(c => {
  65. c.cancel();
  66. });
  67. cancelables = cancelables.filter(r => r.requestId !== cancelRequestId);
  68. }
  69. get(url: string): Promise<any> {
  70. return this.send({ url });
  71. }
  72. async send(options: RequestOptions): Promise<AxiosResponse<any>> {
  73. const cachedData = options.cache
  74. ? cacher.load({ key: options.url, maxAge: options.cacheFor })
  75. : null;
  76. if (cachedData) {
  77. const response: any = { data: cachedData };
  78. return response;
  79. }
  80. const axiosOptions: AxiosRequestConfig = {
  81. url: options.url,
  82. method: options.method || "GET",
  83. headers: options.headers || {},
  84. data: options.data || null,
  85. responseType: options.responseType || "json",
  86. timeout: options.timeout,
  87. };
  88. if (options.cancelId) {
  89. let cancel = () => {};
  90. axiosOptions.cancelToken = new CancelToken(c => {
  91. cancel = c;
  92. });
  93. addCancelable({ requestId: options.cancelId, cancel });
  94. }
  95. if (!options.skipLog) {
  96. logger.log({
  97. url: axiosOptions.url,
  98. method: axiosOptions.method || "GET",
  99. type: "REQUEST",
  100. });
  101. }
  102. const apiCallerHandlers = new ApiCallerHandlers(options, axiosOptions);
  103. try {
  104. const response = await axios(axiosOptions);
  105. if (!options.skipLog) {
  106. console.log(
  107. `%cResponse ${axiosOptions.url}`,
  108. "color: #0044CA",
  109. response.data
  110. );
  111. logger.log({
  112. url: axiosOptions.url,
  113. method: axiosOptions.method || "GET",
  114. type: "RESPONSE",
  115. requestStatus: 200,
  116. });
  117. }
  118. if (options.cache) {
  119. cacher.save({ key: options.url, data: response.data });
  120. }
  121. return response;
  122. } catch (err) {
  123. const error: any = err;
  124. if (error.response) {
  125. throw apiCallerHandlers.handleErrorResponse(error);
  126. } else if (error.request) {
  127. // eslint-disable-next-line @typescript-eslint/no-throw-literal
  128. throw apiCallerHandlers.handleErrorRequest(error);
  129. } else {
  130. throw apiCallerHandlers.handleRequestCancel(error);
  131. }
  132. }
  133. }
  134. setDefaultHeader(name: string, value: string | null) {
  135. axios.defaults.headers.common[name] = value;
  136. }
  137. }
  138. export default new ApiCaller();