@mittwald/api-client-commons 4.168.0 → 4.224.1

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.
@@ -1,11 +1,19 @@
1
- import axios, { Axios } from "axios";
1
+ import axios from "axios";
2
2
  import Request from "./Request.js";
3
3
  export class ApiClientBase {
4
4
  axios;
5
5
  defaultRequestOptions = {};
6
- constructor(axiosConfig = axios) {
7
- this.axios =
8
- axiosConfig instanceof Axios ? axiosConfig : axios.create(axiosConfig);
6
+ constructor(axiosOrConfig = axios) {
7
+ if ("request" in axiosOrConfig &&
8
+ typeof axiosOrConfig.request === "function") {
9
+ this.axios = axiosOrConfig;
10
+ }
11
+ else if (typeof axiosOrConfig === "object") {
12
+ this.axios = axios.create(axiosOrConfig);
13
+ }
14
+ else {
15
+ throw new Error("missing axios instance");
16
+ }
9
17
  }
10
18
  buildRequestOptions(fromRequest) {
11
19
  return {
@@ -1,37 +1,139 @@
1
- import { Axios } from "axios";
2
- import { beforeEach, jest } from "@jest/globals";
1
+ import axios from "axios";
2
+ import { expect, jest } from "@jest/globals";
3
3
  import ApiClientBase from "./ApiClientBase.js";
4
- const requestFn = jest.fn();
5
- const mockedAxios = Object.assign(Object.create(Axios.prototype), {
6
- request: requestFn,
7
- });
4
+ import nock from "nock";
5
+ let apiHistory = [];
8
6
  class TestClient extends ApiClientBase {
9
- testRequest = this.requestFunctionFactory({
10
- path: "/test",
7
+ testRequestWillSucceed = this.requestFunctionFactory({
8
+ path: "/200",
11
9
  method: "GET",
12
10
  operationId: "test",
13
11
  });
12
+ testRequestWillFail = this.requestFunctionFactory({
13
+ path: "/500",
14
+ method: "GET",
15
+ operationId: "fail",
16
+ });
17
+ testNetworkError = this.requestFunctionFactory({
18
+ path: "/networkErrorUrl",
19
+ method: "GET",
20
+ operationId: "networkError",
21
+ });
14
22
  }
15
- const testClient = new TestClient(mockedAxios);
23
+ axios.defaults.baseURL = "http://www.example.com";
24
+ const testClient = new TestClient(axios);
25
+ let testApi;
16
26
  beforeEach(() => {
17
27
  jest.resetAllMocks();
28
+ apiHistory = [];
29
+ testApi = nock("http://www.example.com")
30
+ .get("/200")
31
+ .reply(200, { success: 1 })
32
+ .get("/networkErrorUrl")
33
+ .replyWithError(Object.assign(new Error("Network Error"), { code: "ERR_NETWORK" }))
34
+ .get("/500")
35
+ .reply(500, { fail: 1 });
36
+ testApi.on("request", (req, interceptor, body) => {
37
+ apiHistory.push({
38
+ method: req.method,
39
+ path: req.path,
40
+ body: body,
41
+ headers: req.headers,
42
+ });
43
+ });
18
44
  });
19
45
  test("onBeforeRequest is called before actual request", async () => {
20
46
  const onBeforeRequest = jest.fn(() => {
21
- expect(requestFn).not.toHaveBeenCalled();
47
+ expect(apiHistory).toHaveLength(0);
22
48
  });
23
- await testClient.testRequest(undefined, {
49
+ await testClient.testRequestWillSucceed(undefined, {
24
50
  onBeforeRequest,
25
51
  });
26
- expect(onBeforeRequest).toHaveBeenCalledTimes(1);
27
- expect(requestFn).toHaveBeenCalledTimes(1);
52
+ expect(apiHistory).toHaveLength(1);
28
53
  });
29
54
  test("onBeforeRequest configured in default options is called before actual request", async () => {
30
55
  const onBeforeRequest = jest.fn(() => {
31
- expect(requestFn).not.toHaveBeenCalled();
56
+ expect(apiHistory).toHaveLength(0);
32
57
  });
33
58
  testClient.defaultRequestOptions.onBeforeRequest = onBeforeRequest;
34
- await testClient.testRequest(undefined);
59
+ await testClient.testRequestWillSucceed();
35
60
  expect(onBeforeRequest).toHaveBeenCalledTimes(1);
36
- expect(requestFn).toHaveBeenCalledTimes(1);
61
+ expect(apiHistory).toHaveLength(1);
62
+ });
63
+ test("test client will work with axiosInstance", async () => {
64
+ const axiosInstance = axios.create({
65
+ headers: {
66
+ TEST_FROM_INSTANCE: 1,
67
+ },
68
+ });
69
+ const testClient = new TestClient(axiosInstance);
70
+ await testClient.testRequestWillSucceed();
71
+ expect(apiHistory).toHaveLength(1);
72
+ expect(apiHistory.at(0)).toHaveProperty("headers.test_from_instance", "1");
73
+ });
74
+ test("test client will work with config", async () => {
75
+ const testClient = new TestClient({
76
+ headers: {
77
+ TEST_FROM_CONFIG: 1,
78
+ },
79
+ });
80
+ await testClient.testRequestWillSucceed();
81
+ expect(apiHistory).toHaveLength(1);
82
+ expect(apiHistory.at(0)).toHaveProperty("headers.test_from_config", "1");
83
+ });
84
+ test("test client will work with axios default", async () => {
85
+ axios.defaults.headers = {
86
+ TEST_FROM_DEFAULT: 1,
87
+ };
88
+ const testClient = new TestClient(axios);
89
+ await testClient.testRequestWillSucceed();
90
+ expect(apiHistory).toHaveLength(1);
91
+ expect(apiHistory.at(0)).toHaveProperty("headers.test_from_default", "1");
92
+ });
93
+ test("test client will work with axios default param", async () => {
94
+ axios.defaults.headers = {
95
+ TEST_FROM_DEFAULT_PARAM: 1,
96
+ };
97
+ const testClient = new TestClient();
98
+ await testClient.testRequestWillSucceed();
99
+ expect(apiHistory).toHaveLength(1);
100
+ expect(apiHistory.at(0)).toHaveProperty("headers.test_from_default_param", "1");
101
+ });
102
+ test("test request will resolved with receive data", async () => {
103
+ const response = testClient.testRequestWillSucceed();
104
+ await expect(response).resolves.toHaveProperty("data.success", 1);
105
+ });
106
+ test("test request will be resolved even when status check fails", async () => {
107
+ const success = jest.fn();
108
+ const fail = jest.fn();
109
+ axios.interceptors.response.use((response) => {
110
+ success(response.config.url);
111
+ return response;
112
+ }, (error) => {
113
+ fail(error.config.url);
114
+ throw error;
115
+ });
116
+ const response = testClient.testRequestWillFail();
117
+ // check resolves
118
+ await expect(response).resolves.toHaveProperty("data.fail", 1);
119
+ // check interceptor call
120
+ expect(success).toBeCalledTimes(0);
121
+ expect(fail).toBeCalledWith("500");
122
+ });
123
+ test("test request will rejects on network error", async () => {
124
+ const success = jest.fn();
125
+ const fail = jest.fn();
126
+ axios.interceptors.response.use((response) => {
127
+ success(response.config.url);
128
+ return response;
129
+ }, (error) => {
130
+ fail(error.config.url);
131
+ throw error;
132
+ });
133
+ const response = testClient.testNetworkError();
134
+ // check rejects
135
+ await expect(response).rejects.toThrowError("Network Error");
136
+ // check interceptor call
137
+ expect(success).toBeCalledTimes(0);
138
+ expect(fail).toBeCalledWith("networkErrorUrl");
37
139
  });
@@ -1,4 +1,5 @@
1
1
  import OpenAPIPath from "./OpenAPIPath.js";
2
+ import { AxiosError, } from "axios";
2
3
  export class Request {
3
4
  operationDescriptor;
4
5
  requestObject;
@@ -8,8 +9,18 @@ export class Request {
8
9
  this.requestObject = requestObject;
9
10
  this.requestConfig = this.buildAxiosConfig();
10
11
  }
11
- execute(axios) {
12
- return axios.request(this.requestConfig);
12
+ async execute(axios) {
13
+ try {
14
+ const response = await axios.request(this.requestConfig);
15
+ return response;
16
+ }
17
+ catch (e) {
18
+ const error = AxiosError.from(e);
19
+ if (error.isAxiosError && error.response) {
20
+ return error.response;
21
+ }
22
+ throw e;
23
+ }
13
24
  }
14
25
  buildAxiosConfig() {
15
26
  const { method, path } = this.operationDescriptor;
@@ -36,7 +47,6 @@ export class Request {
36
47
  // Must be a plain object or an URLSearchParams object
37
48
  params,
38
49
  data,
39
- validateStatus: () => true,
40
50
  };
41
51
  }
42
52
  makeAxiosHeaders(headers) {
@@ -4,7 +4,7 @@ import { OpenAPIOperation } from "../types/index.js";
4
4
  export declare abstract class ApiClientBase {
5
5
  readonly axios: AxiosInstance;
6
6
  readonly defaultRequestOptions: RequestOptions;
7
- constructor(axiosConfig?: AxiosInstance | CreateAxiosDefaults);
7
+ constructor(axiosOrConfig?: AxiosInstance | CreateAxiosDefaults);
8
8
  private buildRequestOptions;
9
9
  protected requestFunctionFactory<TOp extends OpenAPIOperation>(operation: TOp): RequestFunction<TOp>;
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/api-client-commons",
3
- "version": "4.168.0",
3
+ "version": "4.224.1",
4
4
  "author": "Mittwald CM Service GmbH & Co. KG <opensource@mittwald.de>",
5
5
  "type": "module",
6
6
  "description": "Common types and utilities for mittwald API clients",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@types/parse-path": "^7.0.3",
44
- "axios": "^1.8.2",
44
+ "axios": "^1.12.2",
45
45
  "parse-path": "^7.0.0",
46
46
  "path-to-regexp": "^8.2.0",
47
47
  "type-fest": "^4.30.0"
@@ -52,11 +52,13 @@
52
52
  "@types/jest": "^29.5.14",
53
53
  "@typescript-eslint/eslint-plugin": "^7.18.0",
54
54
  "@typescript-eslint/parser": "^7.18.0",
55
+ "axios": "^1.12.2",
55
56
  "eslint": "^8.57.1",
56
57
  "eslint-config-prettier": "^9.1.0",
57
58
  "eslint-plugin-json": "^3.1.0",
58
59
  "eslint-plugin-prettier": "^5.2.1",
59
60
  "jest": "^29.7.0",
61
+ "nock": "^14.0.10",
60
62
  "prettier": "^3.4.2",
61
63
  "prettier-plugin-jsdoc": "^1.3.0",
62
64
  "prettier-plugin-pkgsort": "^0.2.1",
@@ -78,5 +80,5 @@
78
80
  "optional": true
79
81
  }
80
82
  },
81
- "gitHead": "5b960bff6080e3f17c8d10feffa6710d72d67280"
83
+ "gitHead": "03179131bc51d51e73f0a35c2be089db76c601a5"
82
84
  }