@dotdev/harmony-sdk 1.22.0 → 1.22.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.
@@ -13,7 +13,7 @@ export declare class HarmonyAPI {
13
13
  private carrierModule;
14
14
  private diaryModule;
15
15
  private giftVoucherModule;
16
- constructor(url: string, axiosRetryConfig?: IAxiosRetryConfig, config?: RequestConfig);
16
+ constructor(url: string, axiosRetryConfig?: IAxiosRetryConfig, requestConfig?: RequestConfig);
17
17
  authenticate(authToken: AuthenticationToken): Promise<string>;
18
18
  stockLookup(params: StockLookupRequestParams, sessionId: string): Promise<Stock[]>;
19
19
  stockCategoryLookup(sessionId: string): Promise<StockCategory[]>;
@@ -3,6 +3,7 @@ import axiosRetry from "axios-retry";
3
3
  import * as https from "https";
4
4
  import { promisify } from "util";
5
5
  import { parseString } from "xml2js";
6
+ import logger from "./helpers/logger";
6
7
  import { AuthModule, CarrierModule, DiaryModule, GiftVoucherModule, PointOfSaleModule, StockClassificationType, StockLevelLookupModule, StockLookupModule, } from "./modules";
7
8
  require("tls").DEFAULT_MIN_VERSION = "TLSv1";
8
9
  const parseXml = promisify(parseString);
@@ -16,12 +17,12 @@ export class HarmonyAPI {
16
17
  carrierModule;
17
18
  diaryModule;
18
19
  giftVoucherModule;
19
- constructor(url, axiosRetryConfig, config) {
20
+ constructor(url, axiosRetryConfig, requestConfig) {
20
21
  this.baseUrl = url;
21
22
  this.axiosInstance = axios.create({
22
23
  baseURL: this.baseUrl,
23
24
  headers: {
24
- ...config?.headers,
25
+ ...requestConfig?.headers,
25
26
  "Content-Type": "text/xml;charset=UTF-8",
26
27
  "Accept-Encoding": "gzip, deflate, br", // Override default Accept-Encoding
27
28
  },
@@ -35,7 +36,10 @@ export class HarmonyAPI {
35
36
  }
36
37
  if (!axiosRetryConfig.onRetry) {
37
38
  axiosRetryConfig.onRetry = (retryCount, error, requestConfig) => {
38
- console.log(`Retrying request (attempt ${retryCount}): ${error}`);
39
+ logger.debug(`Retrying request (attempt ${retryCount}): ${error}`, {
40
+ requestConfig,
41
+ error,
42
+ });
39
43
  };
40
44
  }
41
45
  axiosRetry(this.axiosInstance, axiosRetryConfig);
@@ -9,7 +9,6 @@ const parseXml = promisify(parseString);
9
9
  export class ApiHelper {
10
10
  static async sendSoapRequest(endpoint, axiosInstance, requestBody, type = "POST") {
11
11
  const url = `${endpoint}?wsdl`;
12
- let response;
13
12
  try {
14
13
  let response;
15
14
  switch (type) {
@@ -32,10 +31,16 @@ export class ApiHelper {
32
31
  const logs = logger.child({
33
32
  request: requestBody,
34
33
  response: response?.data,
35
- size: +response?.headers?.["content-length"],
34
+ size: +(response?.headers?.["Content-Length"] || 0),
36
35
  status: response?.status,
37
36
  });
38
37
  logs.debug(`Received Harmony response: `);
38
+ // Throw an error if the response is not in XML format
39
+ if (response?.headers?.["Content-Type"] !== "text/xml" &&
40
+ response?.headers?.["Content-Type"] !== "application/xml") {
41
+ throw new Error(`Returned response not in XML format. This is likely a gateway error - [${response?.status} - ${response?.statusText}]: ${JSON.stringify(response)}`);
42
+ }
43
+ // Parse the XML response
39
44
  const result = (await parseXml(response?.data)); // fix type
40
45
  return result["S:Envelope"]["S:Body"][0];
41
46
  }
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, checkParamLimits, Utils } from ".";
2
+ import { ApiHelper, checkParamLimits, TEST_HEADERS, Utils } from ".";
3
3
  import { ServiceAlias } from "../modules/shared/types";
4
4
  // Increase timeout due to Harmony API having long response time
5
5
  jest.setTimeout(999999);
@@ -35,6 +35,7 @@ describe("ApiHelper", () => {
35
35
  </S:Body>
36
36
  </S:Envelope>
37
37
  `,
38
+ ...TEST_HEADERS,
38
39
  });
39
40
  const response = await ApiHelper.sendSoapRequest(endpoint, mAxios, requestBody);
40
41
  expect(response["ns2:StockClassificationLookupResponse"][0]
@@ -70,3 +70,8 @@ export declare abstract class Utils {
70
70
  */
71
71
  static getFirst(prop: any): string;
72
72
  }
73
+ export declare const TEST_HEADERS: {
74
+ headers: {
75
+ "Content-Type": string;
76
+ };
77
+ };
@@ -138,3 +138,8 @@ export class Utils {
138
138
  return (prop ?? [])[0];
139
139
  }
140
140
  }
141
+ export const TEST_HEADERS = {
142
+ headers: {
143
+ "Content-Type": "text/xml",
144
+ },
145
+ };
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { AuthModule } from "./auth.module";
4
4
  jest.mock("axios");
5
5
  const mAxios = axios;
@@ -30,6 +30,7 @@ describe("AuthModule", () => {
30
30
  </S:Body>
31
31
  </S:Envelope>
32
32
  `,
33
+ ...TEST_HEADERS,
33
34
  };
34
35
  mAxios.post.mockResolvedValueOnce(mockedResponse);
35
36
  const token = await authModule.authenticate(authToken);
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { CarrierModule } from "./carrier.module";
4
4
  jest.mock("axios");
5
5
  const mAxios = axios;
@@ -24,6 +24,7 @@ describe("CarrierModule.getCarrier()", () => {
24
24
  </S:Envelope>
25
25
  `;
26
26
  const mockedResp = {
27
+ ...TEST_HEADERS,
27
28
  data: `
28
29
  <?xml version='1.0' encoding='UTF-8'?>
29
30
  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
@@ -81,6 +82,7 @@ describe("CarrierModule.getCarrierByOrderNumber()", () => {
81
82
  </S:Envelope>
82
83
  `;
83
84
  const mockedResp = {
85
+ ...TEST_HEADERS,
84
86
  data: `
85
87
  <?xml version='1.0' encoding='UTF-8'?>
86
88
  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
@@ -148,6 +150,7 @@ describe("CarrierModule.getCarrierShipmentBySysTime()", () => {
148
150
  </S:Envelope>
149
151
  `;
150
152
  const mockedResp = {
153
+ ...TEST_HEADERS,
151
154
  data: `
152
155
  <?xml version='1.0' encoding='UTF-8'?>
153
156
  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
@@ -212,6 +215,7 @@ describe("CarrierModule.getDispatchDataByOrderNo()", () => {
212
215
  </S:Envelope>
213
216
  `;
214
217
  const mockedResp = {
218
+ ...TEST_HEADERS,
215
219
  data: `
216
220
  <?xml version='1.0' encoding='UTF-8'?>
217
221
  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
@@ -241,20 +245,20 @@ describe("CarrierModule.getDispatchDataByOrderNo()", () => {
241
245
  };
242
246
  const expectedResult = {
243
247
  orderNo: orderNo,
244
- customerOrderNo: 'ORDER',
245
- reference: 'IFX0001103',
246
- carrier: 'APOST',
247
- consignmentNo: 'UTG1347015',
248
- carrierTrackingNo: '',
249
- dispatchDateTime: '23-08-2024 16:11:00',
250
- dispatchLocation: '050',
251
- stock: '5552001.KHAK',
252
- size: '6',
248
+ customerOrderNo: "ORDER",
249
+ reference: "IFX0001103",
250
+ carrier: "APOST",
251
+ consignmentNo: "UTG1347015",
252
+ carrierTrackingNo: "",
253
+ dispatchDateTime: "23-08-2024 16:11:00",
254
+ dispatchLocation: "050",
255
+ stock: "5552001.KHAK",
256
+ size: "6",
253
257
  qdp: 0,
254
258
  pdp: 2,
255
259
  price: 90,
256
260
  shippedQty: 1,
257
- sysModTime: '23-08-2024 16:11:24'
261
+ sysModTime: "23-08-2024 16:11:24",
258
262
  };
259
263
  mAxios.post.mockResolvedValueOnce(mockedResp);
260
264
  const response = await carrierModule.getDispatchDataByOrderNo(orderNo, token);
@@ -300,6 +304,7 @@ describe("CarrierModule.getDispatchDataByTime()", () => {
300
304
  </S:Envelope>
301
305
  `;
302
306
  const mockedResp = {
307
+ ...TEST_HEADERS,
303
308
  data: `
304
309
  <?xml version='1.0' encoding='UTF-8'?>
305
310
  <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
@@ -330,20 +335,20 @@ describe("CarrierModule.getDispatchDataByTime()", () => {
330
335
  };
331
336
  const expectedResult = {
332
337
  orderNo: orderNo,
333
- customerOrderNo: 'ORDER',
334
- reference: 'IFX0001103',
335
- carrier: 'APOST',
336
- consignmentNo: 'UTG1347015',
337
- carrierTrackingNo: '',
338
- dispatchDateTime: '23-08-2024 16:11:00',
339
- dispatchLocation: '050',
340
- stock: '5552001.KHAK',
341
- size: '6',
338
+ customerOrderNo: "ORDER",
339
+ reference: "IFX0001103",
340
+ carrier: "APOST",
341
+ consignmentNo: "UTG1347015",
342
+ carrierTrackingNo: "",
343
+ dispatchDateTime: "23-08-2024 16:11:00",
344
+ dispatchLocation: "050",
345
+ stock: "5552001.KHAK",
346
+ size: "6",
342
347
  qdp: 0,
343
348
  pdp: 2,
344
349
  price: 90,
345
350
  shippedQty: 1,
346
- sysModTime: '23-08-2024 16:11:24'
351
+ sysModTime: "23-08-2024 16:11:24",
347
352
  };
348
353
  mAxios.post.mockResolvedValueOnce(mockedResp);
349
354
  const response = await carrierModule.getDispatchDataByTime(params, token);
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { ServiceAlias } from "../shared";
4
4
  import { DiaryModule } from "./diary.module";
5
5
  import { DiaryTitle } from "./types";
@@ -107,7 +107,7 @@ describe("DiaryModule", () => {
107
107
  </S:Envelope>
108
108
  `;
109
109
  const expectedResult = true;
110
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
110
+ mAxios.post.mockResolvedValueOnce({ data: mockedResp, ...TEST_HEADERS });
111
111
  const response = await diaryModule.createDiary(params, token);
112
112
  console.log(response);
113
113
  // Test that expected response is correct
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { ServiceAlias } from "../shared";
4
4
  import { GiftVoucherModule } from "./gift-voucher.module";
5
5
  jest.mock("axios");
@@ -106,7 +106,10 @@ describe("GiftVoucherModule", () => {
106
106
  countryCode: "",
107
107
  currency: "",
108
108
  };
109
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
109
+ mAxios.post.mockResolvedValueOnce({
110
+ data: mockedResp,
111
+ ...TEST_HEADERS,
112
+ });
110
113
  const response = await gvModule.giftVoucherLookup(params, token);
111
114
  // Test that expected response is correct
112
115
  expect(Array.isArray(response)).toBe(true);
@@ -148,7 +151,10 @@ describe("GiftVoucherModule", () => {
148
151
  </S:Envelope>
149
152
  `;
150
153
  const expectedResult = "9426153259";
151
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
154
+ mAxios.post.mockResolvedValueOnce({
155
+ data: mockedResp,
156
+ ...TEST_HEADERS,
157
+ });
152
158
  const response = await gvModule.giftVoucherVerificationKey(params, token);
153
159
  // Test that expected response is correct
154
160
  expect(typeof response === "string").toBe(true);
@@ -193,7 +199,10 @@ describe("GiftVoucherModule", () => {
193
199
  </S:Envelope>
194
200
  `;
195
201
  const expectedResult = "8951138685";
196
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
202
+ mAxios.post.mockResolvedValueOnce({
203
+ data: mockedResp,
204
+ ...TEST_HEADERS,
205
+ });
197
206
  const response = await gvModule.giftVoucherReservation(params, token);
198
207
  // Test that expected response is correct
199
208
  expect(typeof response === "string").toBe(true);
@@ -238,7 +247,10 @@ describe("GiftVoucherModule", () => {
238
247
  </S:Envelope>
239
248
  `;
240
249
  const expectedResult = 111;
241
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
250
+ mAxios.post.mockResolvedValueOnce({
251
+ data: mockedResp,
252
+ ...TEST_HEADERS,
253
+ });
242
254
  const response = await gvModule.giftVoucherUsed(params, token);
243
255
  // Test that expected response is correct
244
256
  expect(typeof response === "number").toBe(true);
@@ -283,7 +295,7 @@ describe("GiftVoucherModule", () => {
283
295
  </S:Envelope>
284
296
  `;
285
297
  const expectedResult = true;
286
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
298
+ mAxios.post.mockResolvedValueOnce({ data: mockedResp, ...TEST_HEADERS });
287
299
  const response = await gvModule.giftVoucherReservationCancel(params, token);
288
300
  // Test that expected response is correct
289
301
  expect(typeof response === "boolean").toBe(true);
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { ServiceAlias } from "../shared";
4
4
  import { PointOfSaleModule } from "./point-of-sale.module";
5
5
  import { AccountSaleFlag, CardName, PosPaymentType, PosQueryMode, } from "./types";
@@ -192,7 +192,7 @@ describe("PointOfSaleModule", () => {
192
192
  </S:Envelope>
193
193
  `;
194
194
  const expectedResult = true;
195
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
195
+ mAxios.post.mockResolvedValueOnce({ data: mockedResp, ...TEST_HEADERS });
196
196
  const response = await posModule.processSalesOrder(params, token);
197
197
  // Test that expected response is correct
198
198
  expect(typeof response).toEqual("boolean");
@@ -353,7 +353,7 @@ describe("PointOfSaleModule", () => {
353
353
  </S:Envelope>
354
354
  `;
355
355
  const expectedResult = true;
356
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
356
+ mAxios.post.mockResolvedValueOnce({ data: mockedResp, ...TEST_HEADERS });
357
357
  const response = await posModule.processSalesOrderWithoutPayment(params, token);
358
358
  // Test that expected response is correct
359
359
  expect(typeof response).toEqual("boolean");
@@ -502,7 +502,7 @@ describe("PointOfSaleModule", () => {
502
502
  invoicePriceInc: 100.1,
503
503
  rounding: 1.1,
504
504
  totalTax: 2.1,
505
- cashDrawerId: 'BIONLINE'
505
+ cashDrawerId: "BIONLINE",
506
506
  },
507
507
  extra: {
508
508
  deliveryAddress1: "37",
@@ -554,7 +554,7 @@ describe("PointOfSaleModule", () => {
554
554
  </S:Envelope>
555
555
  `;
556
556
  const expectedResult = true;
557
- mAxios.post.mockResolvedValueOnce({ data: mockedResp });
557
+ mAxios.post.mockResolvedValueOnce({ data: mockedResp, ...TEST_HEADERS });
558
558
  const response = await posModule.processInvoice(params, token);
559
559
  // Test that expected response is correct
560
560
  expect(typeof response).toEqual("boolean");
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import { ApiHelper, Utils } from "../../helpers";
2
+ import { ApiHelper, TEST_HEADERS, Utils } from "../../helpers";
3
3
  import { ServiceAlias } from "../shared";
4
4
  import { StockSearchType } from "../stock-lookup";
5
5
  import { StockLevelLookupModule } from "./stock-level-lookup.module";
@@ -46,6 +46,7 @@ describe("StockLevelLookupModule.warehouseLookup()", () => {
46
46
  </S:Body>
47
47
  </S:Envelope>
48
48
  `,
49
+ ...TEST_HEADERS,
49
50
  };
50
51
  const expectedResponse = {
51
52
  namekey: "000",
@@ -152,6 +153,7 @@ describe("StockLevelLookupModule.stockLevelLookup()", () => {
152
153
  </S:Body>
153
154
  </S:Envelope>
154
155
  `,
156
+ ...TEST_HEADERS,
155
157
  };
156
158
  const expectedResult = {
157
159
  stock: "4001003.BLK",
@@ -272,6 +274,7 @@ describe("StockLevelLookupModule.stockLevelLookup()", () => {
272
274
  </S:Body>
273
275
  </S:Envelope>
274
276
  `,
277
+ ...TEST_HEADERS,
275
278
  };
276
279
  const expectedResult = {
277
280
  stock: "4001003.BLK",
@@ -43,6 +43,9 @@ describe("SizeGridLookup", () => {
43
43
  </S:Body>
44
44
  </S:Envelope>
45
45
  `,
46
+ headers: {
47
+ "Content-Type": "text/xml",
48
+ },
46
49
  };
47
50
  const expected = {
48
51
  namekey: "01",
@@ -96,6 +99,9 @@ describe("StockClassificationLookup", () => {
96
99
  </S:Body>
97
100
  </S:Envelope>
98
101
  `,
102
+ headers: {
103
+ "Content-Type": "text/xml",
104
+ },
99
105
  };
100
106
  const mockedOutput = [
101
107
  { code: "BACK", description: "BACK PACK" },
@@ -127,6 +133,9 @@ describe("StockBarcodeLookup", () => {
127
133
  </ns2:StockBarcodeLookupResponse>
128
134
  </S:Body>
129
135
  </S:Envelope>`,
136
+ headers: {
137
+ "Content-Type": "text/xml",
138
+ },
130
139
  };
131
140
  const mockedOutput = [
132
141
  {
@@ -167,6 +176,9 @@ describe("StockBarcodeLookup", () => {
167
176
  </ns2:StockBarcodeLookupResponse>
168
177
  </S:Body>
169
178
  </S:Envelope>`,
179
+ headers: {
180
+ "Content-Type": "text/xml",
181
+ },
170
182
  };
171
183
  mAxios.post.mockResolvedValueOnce(resp);
172
184
  const response = await stockLookupModule.stockBarcodeLookup({
@@ -239,6 +251,9 @@ describe("StockColorLookup", () => {
239
251
  </S:Body>
240
252
  </S:Envelope>
241
253
  `,
254
+ headers: {
255
+ "Content-Type": "text/xml",
256
+ },
242
257
  };
243
258
  const mockedOutput = [
244
259
  {
@@ -272,6 +287,9 @@ describe("StockCatergoryLookup", () => {
272
287
  </S:Body>
273
288
  </S:Envelope>
274
289
  `,
290
+ headers: {
291
+ "Content-Type": "text/xml",
292
+ },
275
293
  };
276
294
  const mockedOutput = [
277
295
  {
@@ -356,6 +374,9 @@ describe("StockLookup", () => {
356
374
  </S:Body>
357
375
  </S:Envelope>
358
376
  `,
377
+ headers: {
378
+ "Content-Type": "text/xml",
379
+ },
359
380
  };
360
381
  const mockedOutput = [
361
382
  {
@@ -412,6 +433,7 @@ describe("StockLookup", () => {
412
433
  ];
413
434
  mAxios.post.mockResolvedValueOnce(resp);
414
435
  const response = await stockLookupModule.stockLookup(params, `"${sessionId}"`);
436
+ console.log(response);
415
437
  expect(Array.isArray(response)).toEqual(true);
416
438
  expect(response.length).toEqual(1);
417
439
  expect(response).toEqual(mockedOutput);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotdev/harmony-sdk",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "description": "Harmony API SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",