@odata2ts/http-client-axios 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/lib/AxiosClient.d.ts +9 -20
- package/lib/AxiosClient.js +17 -80
- package/lib/AxiosClient.js.map +1 -1
- package/lib/AxiosClientError.d.ts +6 -4
- package/lib/AxiosClientError.js +3 -1
- package/lib/AxiosClientError.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [0.8.0](https://github.com/odata2ts/http-client/compare/@odata2ts/http-client-axios@0.7.0...@odata2ts/http-client-axios@0.8.0) (2023-06-10)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* conventionalize client errors ([#5](https://github.com/odata2ts/http-client/issues/5)) ([a8e8912](https://github.com/odata2ts/http-client/commit/a8e89125eeda47436d48507d6a71efc90953f878))
|
|
11
|
+
|
|
6
12
|
# 0.7.0 (2023-06-03)
|
|
7
13
|
|
|
8
14
|
### Features
|
package/lib/AxiosClient.d.ts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
import { HttpResponseModel
|
|
1
|
+
import { HttpResponseModel } from "@odata2ts/http-client-api";
|
|
2
|
+
import { BaseHttpClient, BaseHttpClientOptions, HttpMethods } from "@odata2ts/http-client-base";
|
|
3
|
+
import { AxiosInstance, AxiosResponseHeaders, RawAxiosResponseHeaders } from "axios";
|
|
2
4
|
import { AxiosRequestConfig } from "./AxiosRequestConfig";
|
|
3
|
-
export
|
|
4
|
-
export interface ClientOptions {
|
|
5
|
-
useCsrfProtection?: boolean;
|
|
6
|
-
csrfTokenFetchUrl?: string;
|
|
5
|
+
export interface ClientOptions extends BaseHttpClientOptions {
|
|
7
6
|
}
|
|
8
7
|
export declare const DEFAULT_ERROR_MESSAGE = "No error message!";
|
|
9
|
-
export declare
|
|
10
|
-
export declare class AxiosClient implements ODataHttpClient<AxiosRequestConfig> {
|
|
8
|
+
export declare class AxiosClient extends BaseHttpClient<AxiosRequestConfig> {
|
|
11
9
|
private clientOptions?;
|
|
12
|
-
|
|
13
|
-
private csrfToken;
|
|
14
|
-
private getErrorMessage;
|
|
10
|
+
protected readonly client: AxiosInstance;
|
|
15
11
|
constructor(config?: AxiosRequestConfig, clientOptions?: ClientOptions | undefined);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
private sendRequest;
|
|
20
|
-
post<ResponseModel>(url: string, data: any, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<ResponseModel>>;
|
|
21
|
-
get<ResponseModel>(url: string, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<ResponseModel>>;
|
|
22
|
-
put<ResponseModel>(url: string, data: any, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<ResponseModel>>;
|
|
23
|
-
patch<ResponseModel>(url: string, data: any, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<ResponseModel>>;
|
|
24
|
-
merge<ResponseModel>(url: string, data: any, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<ResponseModel>>;
|
|
25
|
-
delete(url: string, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<void>>;
|
|
12
|
+
protected addHeaderToRequestConfig(headers: Record<string, string>, config: AxiosRequestConfig | undefined): AxiosRequestConfig;
|
|
13
|
+
protected executeRequest<ResponseModel>(method: HttpMethods, url: string, data: any, requestConfig?: AxiosRequestConfig | undefined): Promise<HttpResponseModel<ResponseModel>>;
|
|
14
|
+
protected mapHeaders(headers: AxiosResponseHeaders | RawAxiosResponseHeaders): Record<string, string>;
|
|
26
15
|
}
|
package/lib/AxiosClient.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AxiosClient = exports.
|
|
3
|
+
exports.AxiosClient = exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const http_client_base_1 = require("@odata2ts/http-client-base");
|
|
5
6
|
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
6
7
|
const AxiosClientError_1 = require("./AxiosClientError");
|
|
7
8
|
const AxiosRequestConfig_1 = require("./AxiosRequestConfig");
|
|
8
9
|
exports.DEFAULT_ERROR_MESSAGE = "No error message!";
|
|
9
|
-
const FAILURE_MISSING_CSRF_URL = "When automatic CSRF token handling is activated, the URL must be supplied via attribute [csrfTokenFetchUrl]!";
|
|
10
10
|
const FAILURE_NO_RESPONSE = "No response from server! Failure: ";
|
|
11
11
|
const FAILURE_NO_REQUEST = "No request was sent! Failure: ";
|
|
12
12
|
const FAILURE_RESPONSE_MESSAGE = "OData server responded with error: ";
|
|
@@ -15,56 +15,21 @@ function buildErrorMessage(prefix, error) {
|
|
|
15
15
|
const msg = typeof error === "string" ? error : error === null || error === void 0 ? void 0 : error.message;
|
|
16
16
|
return prefix + (msg || exports.DEFAULT_ERROR_MESSAGE);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
var _a;
|
|
20
|
-
const eMsg = (_a = responseData === null || responseData === void 0 ? void 0 : responseData.error) === null || _a === void 0 ? void 0 : _a.message;
|
|
21
|
-
return typeof (eMsg === null || eMsg === void 0 ? void 0 : eMsg.value) === "string" ? eMsg.value : eMsg;
|
|
22
|
-
};
|
|
23
|
-
exports.getV2OrV4ErrorMessage = getV2OrV4ErrorMessage;
|
|
24
|
-
class AxiosClient {
|
|
18
|
+
class AxiosClient extends http_client_base_1.BaseHttpClient {
|
|
25
19
|
constructor(config, clientOptions) {
|
|
26
|
-
|
|
20
|
+
super(clientOptions);
|
|
27
21
|
this.clientOptions = clientOptions;
|
|
28
|
-
this.getErrorMessage = exports.getV2OrV4ErrorMessage;
|
|
29
22
|
this.client = axios_1.default.create((0, AxiosRequestConfig_1.getDefaultConfig)(config));
|
|
30
|
-
if (clientOptions && clientOptions.useCsrfProtection && !((_a = clientOptions.csrfTokenFetchUrl) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
31
|
-
throw new Error(FAILURE_MISSING_CSRF_URL);
|
|
32
|
-
}
|
|
33
23
|
}
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
addHeaderToRequestConfig(headers, config) {
|
|
25
|
+
return (0, AxiosRequestConfig_1.mergeConfig)(config, { headers });
|
|
36
26
|
}
|
|
37
|
-
|
|
27
|
+
executeRequest(method, url, data, requestConfig = {}) {
|
|
38
28
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
fetchSecurityToken() {
|
|
46
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
47
|
-
const fetchUrl = this.clientOptions.csrfTokenFetchUrl;
|
|
48
|
-
const response = yield this.get(fetchUrl, { headers: { "x-csrf-token": "Fetch" } });
|
|
49
|
-
return response.headers["x-csrf-token"];
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
sendRequest(config) {
|
|
53
|
-
var _a, _b, _c;
|
|
54
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
55
|
-
if (typeof config.url !== "string") {
|
|
56
|
-
throw new Error("Value for URL must be provided!");
|
|
57
|
-
}
|
|
58
|
-
// setup automatic CSRF token handling
|
|
59
|
-
if (((_a = this.clientOptions) === null || _a === void 0 ? void 0 : _a.useCsrfProtection) &&
|
|
60
|
-
["POST", "PUT", "PATCH", "DELETE"].includes(config.method.toUpperCase())) {
|
|
61
|
-
const csrfToken = yield this.setupSecurityToken();
|
|
62
|
-
if (typeof csrfToken === "string") {
|
|
63
|
-
if (!config.headers) {
|
|
64
|
-
config.headers = {};
|
|
65
|
-
}
|
|
66
|
-
config.headers["x-csrf-token"] = csrfToken;
|
|
67
|
-
}
|
|
29
|
+
// add URL and HTTP method to the request config
|
|
30
|
+
const config = (0, AxiosRequestConfig_1.mergeConfig)(requestConfig, { url, method });
|
|
31
|
+
if (typeof data !== "undefined") {
|
|
32
|
+
config.data = data;
|
|
68
33
|
}
|
|
69
34
|
try {
|
|
70
35
|
return yield this.client.request(config);
|
|
@@ -72,51 +37,23 @@ class AxiosClient {
|
|
|
72
37
|
catch (error) {
|
|
73
38
|
if (error.isAxiosError) {
|
|
74
39
|
const axiosError = error;
|
|
75
|
-
// automatic CSRF token handling
|
|
76
|
-
// csrf token expired, let's reset it and perform the original request again
|
|
77
|
-
if (((_b = this.clientOptions) === null || _b === void 0 ? void 0 : _b.useCsrfProtection) &&
|
|
78
|
-
((_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === 403 &&
|
|
79
|
-
axiosError.response.headers["x-csrf-token"] === "Required") {
|
|
80
|
-
this.csrfToken = undefined;
|
|
81
|
-
return this.sendRequest(config);
|
|
82
|
-
}
|
|
83
40
|
// regular failure handling
|
|
84
41
|
if (axiosError.response) {
|
|
85
|
-
const msg = buildErrorMessage(FAILURE_RESPONSE_MESSAGE, this.
|
|
86
|
-
throw new AxiosClientError_1.AxiosClientError(msg, axiosError.response.status, axiosError);
|
|
42
|
+
const msg = buildErrorMessage(FAILURE_RESPONSE_MESSAGE, this.retrieveErrorMessage(axiosError.response.data));
|
|
43
|
+
throw new AxiosClientError_1.AxiosClientError(msg, axiosError.response.status, this.mapHeaders(axiosError.response.headers), axiosError);
|
|
87
44
|
}
|
|
88
45
|
// fatal failure without response
|
|
89
46
|
else {
|
|
90
|
-
throw new AxiosClientError_1.AxiosClientError(buildErrorMessage(axiosError.request ? FAILURE_NO_RESPONSE : FAILURE_NO_REQUEST, axiosError), undefined, axiosError);
|
|
47
|
+
throw new AxiosClientError_1.AxiosClientError(buildErrorMessage(axiosError.request ? FAILURE_NO_RESPONSE : FAILURE_NO_REQUEST, axiosError), undefined, undefined, axiosError);
|
|
91
48
|
}
|
|
92
49
|
}
|
|
93
50
|
// not an Axios error
|
|
94
|
-
throw new AxiosClientError_1.AxiosClientError(buildErrorMessage(FAILURE_AXIOS, error), undefined, error);
|
|
51
|
+
throw new AxiosClientError_1.AxiosClientError(buildErrorMessage(FAILURE_AXIOS, error), undefined, undefined, error);
|
|
95
52
|
}
|
|
96
53
|
});
|
|
97
54
|
}
|
|
98
|
-
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
get(url, requestConfig) {
|
|
102
|
-
return this.sendRequest(Object.assign(Object.assign({}, requestConfig), { url, method: "GET" }));
|
|
103
|
-
}
|
|
104
|
-
put(url, data, requestConfig) {
|
|
105
|
-
return this.sendRequest(Object.assign(Object.assign({}, requestConfig), { url, data, method: "PUT" }));
|
|
106
|
-
}
|
|
107
|
-
patch(url, data, requestConfig) {
|
|
108
|
-
return this.sendRequest(Object.assign(Object.assign({}, requestConfig), { url, data, method: "PATCH" }));
|
|
109
|
-
}
|
|
110
|
-
merge(url, data, requestConfig) {
|
|
111
|
-
return this.sendRequest((0, AxiosRequestConfig_1.mergeConfig)(requestConfig, {
|
|
112
|
-
url,
|
|
113
|
-
method: "POST",
|
|
114
|
-
headers: { "X-Http-Method": "MERGE" },
|
|
115
|
-
data,
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
delete(url, requestConfig) {
|
|
119
|
-
return this.sendRequest(Object.assign(Object.assign({}, requestConfig), { url, method: "DELETE" }));
|
|
55
|
+
mapHeaders(headers) {
|
|
56
|
+
return headers;
|
|
120
57
|
}
|
|
121
58
|
}
|
|
122
59
|
exports.AxiosClient = AxiosClient;
|
package/lib/AxiosClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AxiosClient.js","sourceRoot":"","sources":["../src/AxiosClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpResponseModel, ODataHttpClient } from \"@odata2ts/http-client-api\";\nimport axios, { AxiosError, AxiosInstance, AxiosRequestConfig as OriginalRequestConfig } from \"axios\";\n\nimport { AxiosClientError } from \"./AxiosClientError\";\nimport { AxiosRequestConfig, InternalRequestConfig, getDefaultConfig, mergeConfig } from \"./AxiosRequestConfig\";\n\nexport type ErrorMessageRetriever<ResponseType = any> = (error: any) => string | null | undefined;\n\nexport interface ClientOptions {\n useCsrfProtection?: boolean;\n csrfTokenFetchUrl?: string;\n}\n\nexport const DEFAULT_ERROR_MESSAGE = \"No error message!\";\nconst FAILURE_MISSING_CSRF_URL =\n \"When automatic CSRF token handling is activated, the URL must be supplied via attribute [csrfTokenFetchUrl]!\";\nconst FAILURE_NO_RESPONSE = \"No response from server! Failure: \";\nconst FAILURE_NO_REQUEST = \"No request was sent! Failure: \";\nconst FAILURE_RESPONSE_MESSAGE = \"OData server responded with error: \";\nconst FAILURE_AXIOS = \"Fatal Axios failure: \";\n\nfunction buildErrorMessage(prefix: string, error: any) {\n const msg = typeof error === \"string\" ? error : (error as Error)?.message;\n return prefix + (msg || DEFAULT_ERROR_MESSAGE);\n}\n\nexport const getV2OrV4ErrorMessage: ErrorMessageRetriever = (responseData: any): string | undefined => {\n const eMsg = responseData?.error?.message;\n return typeof eMsg?.value === \"string\" ? eMsg.value : eMsg;\n};\n\nexport class AxiosClient implements ODataHttpClient<AxiosRequestConfig> {\n private readonly client: AxiosInstance;\n private csrfToken: string | undefined;\n private getErrorMessage: ErrorMessageRetriever = getV2OrV4ErrorMessage;\n\n constructor(config?: AxiosRequestConfig, private clientOptions?: ClientOptions) {\n this.client = axios.create(getDefaultConfig(config));\n\n if (clientOptions && clientOptions.useCsrfProtection && !clientOptions.csrfTokenFetchUrl?.trim()) {\n throw new Error(FAILURE_MISSING_CSRF_URL);\n }\n }\n\n public setErrorMessageRetriever<T = any>(getErrorMsg: ErrorMessageRetriever<T>) {\n this.getErrorMessage = getErrorMsg;\n }\n\n private async setupSecurityToken() {\n if (!this.csrfToken) {\n this.csrfToken = await this.fetchSecurityToken();\n }\n return this.csrfToken;\n }\n\n private async fetchSecurityToken(): Promise<string | undefined> {\n const fetchUrl = this.clientOptions!.csrfTokenFetchUrl!;\n const response = await this.get(fetchUrl, { headers: { \"x-csrf-token\": \"Fetch\" } });\n\n return response.headers[\"x-csrf-token\"];\n }\n\n private async sendRequest<ResponseType>(config: InternalRequestConfig): Promise<HttpResponseModel<ResponseType>> {\n if (typeof config.url !== \"string\") {\n throw new Error(\"Value for URL must be provided!\");\n }\n\n // setup automatic CSRF token handling\n if (\n this.clientOptions?.useCsrfProtection &&\n [\"POST\", \"PUT\", \"PATCH\", \"DELETE\"].includes(config.method!.toUpperCase())\n ) {\n const csrfToken = await this.setupSecurityToken();\n if (typeof csrfToken === \"string\") {\n if (!config.headers) {\n config.headers = {};\n }\n config.headers![\"x-csrf-token\"] = csrfToken;\n }\n }\n\n try {\n return await this.client.request(config as OriginalRequestConfig);\n } catch (error: any) {\n if ((error as AxiosError).isAxiosError) {\n const axiosError = error as AxiosError;\n\n // automatic CSRF token handling\n // csrf token expired, let's reset it and perform the original request again\n if (\n this.clientOptions?.useCsrfProtection &&\n axiosError.response?.status === 403 &&\n axiosError.response.headers[\"x-csrf-token\"] === \"Required\"\n ) {\n this.csrfToken = undefined;\n return this.sendRequest<ResponseType>(config);\n }\n\n // regular failure handling\n if (axiosError.response) {\n const msg = buildErrorMessage(FAILURE_RESPONSE_MESSAGE, this.getErrorMessage(axiosError.response.data));\n throw new AxiosClientError(msg, axiosError.response.status, axiosError);\n }\n // fatal failure without response\n else {\n throw new AxiosClientError(\n buildErrorMessage(axiosError.request ? FAILURE_NO_RESPONSE : FAILURE_NO_REQUEST, axiosError),\n undefined,\n axiosError\n );\n }\n }\n // not an Axios error\n throw new AxiosClientError(buildErrorMessage(FAILURE_AXIOS, error), undefined, error);\n }\n }\n\n public post<ResponseModel>(\n url: string,\n data: any,\n requestConfig?: AxiosRequestConfig\n ): Promise<HttpResponseModel<ResponseModel>> {\n return this.sendRequest({ ...requestConfig, url, data, method: \"POST\" });\n }\n public get<ResponseModel>(\n url: string,\n requestConfig?: AxiosRequestConfig\n ): Promise<HttpResponseModel<ResponseModel>> {\n return this.sendRequest({ ...requestConfig, url, method: \"GET\" });\n }\n public put<ResponseModel>(\n url: string,\n data: any,\n requestConfig?: AxiosRequestConfig\n ): Promise<HttpResponseModel<ResponseModel>> {\n return this.sendRequest({ ...requestConfig, url, data, method: \"PUT\" });\n }\n public patch<ResponseModel>(\n url: string,\n data: any,\n requestConfig?: AxiosRequestConfig\n ): Promise<HttpResponseModel<ResponseModel>> {\n return this.sendRequest({ ...requestConfig, url, data, method: \"PATCH\" });\n }\n public merge<ResponseModel>(\n url: string,\n data: any,\n requestConfig?: AxiosRequestConfig\n ): Promise<HttpResponseModel<ResponseModel>> {\n return this.sendRequest(\n mergeConfig(requestConfig, {\n url,\n method: \"POST\",\n headers: { \"X-Http-Method\": \"MERGE\" },\n data,\n })\n );\n }\n public delete(url: string, requestConfig?: AxiosRequestConfig): Promise<HttpResponseModel<void>> {\n return this.sendRequest({ ...requestConfig, url, method: \"DELETE\" });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AxiosClient.js","sourceRoot":"","sources":["../src/AxiosClient.ts"],"names":[],"mappings":";;;;AACA,iEAAgG;AAChG,0DAMe;AAEf,yDAAsD;AACtD,6DAAyF;AAI5E,QAAA,qBAAqB,GAAG,mBAAmB,CAAC;AACzD,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AACjE,MAAM,kBAAkB,GAAG,gCAAgC,CAAC;AAC5D,MAAM,wBAAwB,GAAG,qCAAqC,CAAC;AACvE,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,SAAS,iBAAiB,CAAC,MAAc,EAAE,KAAU;IACnD,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,KAAe,aAAf,KAAK,uBAAL,KAAK,CAAY,OAAO,CAAC;IAC1E,OAAO,MAAM,GAAG,CAAC,GAAG,IAAI,6BAAqB,CAAC,CAAC;AACjD,CAAC;AAED,MAAa,WAAY,SAAQ,iCAAkC;IAGjE,YAAY,MAA2B,EAAU,aAA6B;QAC5E,KAAK,CAAC,aAAa,CAAC,CAAC;QAD0B,kBAAa,GAAb,aAAa,CAAgB;QAE5E,IAAI,CAAC,MAAM,GAAG,eAAK,CAAC,MAAM,CAAC,IAAA,qCAAgB,EAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAES,wBAAwB,CAChC,OAA+B,EAC/B,MAAsC;QAEtC,OAAO,IAAA,gCAAW,EAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;IAEe,cAAc,CAC5B,MAAmB,EACnB,GAAW,EACX,IAAS,EACT,gBAAgD,EAAE;;YAElD,gDAAgD;YAChD,MAAM,MAAM,GAA0B,IAAA,gCAAW,EAAC,aAAa,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YAClF,IAAI,OAAO,IAAI,KAAK,WAAW,EAAE;gBAC/B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;aACpB;YAED,IAAI;gBACF,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;aAC1C;YAAC,OAAO,KAAU,EAAE;gBACnB,IAAK,KAAoB,CAAC,YAAY,EAAE;oBACtC,MAAM,UAAU,GAAG,KAAmB,CAAC;oBAEvC,2BAA2B;oBAC3B,IAAI,UAAU,CAAC,QAAQ,EAAE;wBACvB,MAAM,GAAG,GAAG,iBAAiB,CAAC,wBAAwB,EAAE,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;wBAC7G,MAAM,IAAI,mCAAgB,CACxB,GAAG,EACH,UAAU,CAAC,QAAQ,CAAC,MAAM,EAC1B,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAC5C,UAAU,CACX,CAAC;qBACH;oBACD,iCAAiC;yBAC5B;wBACH,MAAM,IAAI,mCAAgB,CACxB,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,kBAAkB,EAAE,UAAU,CAAC,EAC5F,SAAS,EACT,SAAS,EACT,UAAU,CACX,CAAC;qBACH;iBACF;gBACD,qBAAqB;gBACrB,MAAM,IAAI,mCAAgB,CAAC,iBAAiB,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;aAClG;QACH,CAAC;KAAA;IAES,UAAU,CAAC,OAAuD;QAC1E,OAAO,OAAiC,CAAC;IAC3C,CAAC;CACF;AA7DD,kCA6DC","sourcesContent":["import { HttpResponseModel } from \"@odata2ts/http-client-api\";\r\nimport { BaseHttpClient, BaseHttpClientOptions, HttpMethods } from \"@odata2ts/http-client-base\";\r\nimport axios, {\r\n AxiosError,\r\n AxiosInstance,\r\n AxiosResponseHeaders,\r\n AxiosRequestConfig as OriginalRequestConfig,\r\n RawAxiosResponseHeaders,\r\n} from \"axios\";\r\n\r\nimport { AxiosClientError } from \"./AxiosClientError\";\r\nimport { AxiosRequestConfig, getDefaultConfig, mergeConfig } from \"./AxiosRequestConfig\";\r\n\r\nexport interface ClientOptions extends BaseHttpClientOptions {}\r\n\r\nexport const DEFAULT_ERROR_MESSAGE = \"No error message!\";\r\nconst FAILURE_NO_RESPONSE = \"No response from server! Failure: \";\r\nconst FAILURE_NO_REQUEST = \"No request was sent! Failure: \";\r\nconst FAILURE_RESPONSE_MESSAGE = \"OData server responded with error: \";\r\nconst FAILURE_AXIOS = \"Fatal Axios failure: \";\r\n\r\nfunction buildErrorMessage(prefix: string, error: any) {\r\n const msg = typeof error === \"string\" ? error : (error as Error)?.message;\r\n return prefix + (msg || DEFAULT_ERROR_MESSAGE);\r\n}\r\n\r\nexport class AxiosClient extends BaseHttpClient<AxiosRequestConfig> {\r\n protected readonly client: AxiosInstance;\r\n\r\n constructor(config?: AxiosRequestConfig, private clientOptions?: ClientOptions) {\r\n super(clientOptions);\r\n this.client = axios.create(getDefaultConfig(config));\r\n }\r\n\r\n protected addHeaderToRequestConfig(\r\n headers: Record<string, string>,\r\n config: AxiosRequestConfig | undefined\r\n ): AxiosRequestConfig {\r\n return mergeConfig(config, { headers });\r\n }\r\n\r\n protected async executeRequest<ResponseModel>(\r\n method: HttpMethods,\r\n url: string,\r\n data: any,\r\n requestConfig: AxiosRequestConfig | undefined = {}\r\n ): Promise<HttpResponseModel<ResponseModel>> {\r\n // add URL and HTTP method to the request config\r\n const config: OriginalRequestConfig = mergeConfig(requestConfig, { url, method });\r\n if (typeof data !== \"undefined\") {\r\n config.data = data;\r\n }\r\n\r\n try {\r\n return await this.client.request(config);\r\n } catch (error: any) {\r\n if ((error as AxiosError).isAxiosError) {\r\n const axiosError = error as AxiosError;\r\n\r\n // regular failure handling\r\n if (axiosError.response) {\r\n const msg = buildErrorMessage(FAILURE_RESPONSE_MESSAGE, this.retrieveErrorMessage(axiosError.response.data));\r\n throw new AxiosClientError(\r\n msg,\r\n axiosError.response.status,\r\n this.mapHeaders(axiosError.response.headers),\r\n axiosError\r\n );\r\n }\r\n // fatal failure without response\r\n else {\r\n throw new AxiosClientError(\r\n buildErrorMessage(axiosError.request ? FAILURE_NO_RESPONSE : FAILURE_NO_REQUEST, axiosError),\r\n undefined,\r\n undefined,\r\n axiosError\r\n );\r\n }\r\n }\r\n // not an Axios error\r\n throw new AxiosClientError(buildErrorMessage(FAILURE_AXIOS, error), undefined, undefined, error);\r\n }\r\n }\r\n\r\n protected mapHeaders(headers: AxiosResponseHeaders | RawAxiosResponseHeaders): Record<string, string> {\r\n return headers as Record<string, string>;\r\n }\r\n}\r\n"]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { ODataClientError } from "@odata2ts/http-client-api";
|
|
1
2
|
import type { AxiosError } from "axios";
|
|
2
|
-
export declare class AxiosClientError extends Error {
|
|
3
|
-
readonly status
|
|
4
|
-
readonly
|
|
5
|
-
|
|
3
|
+
export declare class AxiosClientError extends Error implements ODataClientError {
|
|
4
|
+
readonly status?: number | undefined;
|
|
5
|
+
readonly headers?: Record<string, string> | undefined;
|
|
6
|
+
readonly cause?: Error | AxiosError<unknown, any> | undefined;
|
|
7
|
+
constructor(msg: string, status?: number | undefined, headers?: Record<string, string> | undefined, cause?: Error | AxiosError<unknown, any> | undefined);
|
|
6
8
|
}
|
package/lib/AxiosClientError.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AxiosClientError = void 0;
|
|
4
4
|
class AxiosClientError extends Error {
|
|
5
|
-
constructor(msg, status, cause) {
|
|
5
|
+
constructor(msg, status, headers, cause) {
|
|
6
|
+
// @ts-ignore: fetch requires lib "dom" or "webworker", but then the "cause" property becomes unknown to TS
|
|
6
7
|
super(msg, { cause });
|
|
7
8
|
this.status = status;
|
|
9
|
+
this.headers = headers;
|
|
8
10
|
this.cause = cause;
|
|
9
11
|
this.name = this.constructor.name;
|
|
10
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AxiosClientError.js","sourceRoot":"","sources":["../src/AxiosClientError.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"AxiosClientError.js","sourceRoot":"","sources":["../src/AxiosClientError.ts"],"names":[],"mappings":";;;AAGA,MAAa,gBAAiB,SAAQ,KAAK;IACzC,YACE,GAAW,EACK,MAAe,EACf,OAAgC,EAChC,KAA0B;QAE1C,2GAA2G;QAC3G,KAAK,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QALN,WAAM,GAAN,MAAM,CAAS;QACf,YAAO,GAAP,OAAO,CAAyB;QAChC,UAAK,GAAL,KAAK,CAAqB;QAI1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IACpC,CAAC;CACF;AAXD,4CAWC","sourcesContent":["import { ODataClientError } from \"@odata2ts/http-client-api\";\r\nimport type { AxiosError } from \"axios\";\r\n\r\nexport class AxiosClientError extends Error implements ODataClientError {\r\n constructor(\r\n msg: string,\r\n public readonly status?: number,\r\n public readonly headers?: Record<string, string>,\r\n public readonly cause?: Error | AxiosError\r\n ) {\r\n // @ts-ignore: fetch requires lib \"dom\" or \"webworker\", but then the \"cause\" property becomes unknown to TS\r\n super(msg, { cause });\r\n this.name = this.constructor.name;\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odata2ts/http-client-axios",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"axios": "^0.27.0 || ^1.0.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@odata2ts/http-client-
|
|
37
|
+
"@odata2ts/http-client-base": "^0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/jest": "^27.4.1",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"typescript": "5.0.4"
|
|
48
48
|
},
|
|
49
49
|
"types": "./lib/index.d.ts",
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "485559dce9388b480c7d0b3ded2fe2f3f5f98fad"
|
|
51
51
|
}
|