@dotdev/harmony-sdk 1.0.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.
- package/README.md +132 -0
- package/dist/HarmonyAPI.d.ts +37 -0
- package/dist/HarmonyAPI.js +113 -0
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.js +18 -0
- package/dist/helpers/index.d.ts +50 -0
- package/dist/helpers/index.js +167 -0
- package/dist/helpers/index.spec.d.ts +1 -0
- package/dist/helpers/index.spec.js +147 -0
- package/dist/helpers/mapper.d.ts +2 -0
- package/dist/helpers/mapper.js +28 -0
- package/dist/helpers/utils.d.ts +71 -0
- package/dist/helpers/utils.js +133 -0
- package/dist/helpers/utils.spec.d.ts +1 -0
- package/dist/helpers/utils.spec.js +96 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/modules/auth/auth.module.d.ts +7 -0
- package/dist/modules/auth/auth.module.js +42 -0
- package/dist/modules/auth/auth.module.spec.d.ts +1 -0
- package/dist/modules/auth/auth.module.spec.js +55 -0
- package/dist/modules/auth/index.d.ts +2 -0
- package/dist/modules/auth/index.js +2 -0
- package/dist/modules/auth/types/index.d.ts +7 -0
- package/dist/modules/auth/types/index.js +1 -0
- package/dist/modules/carrier/carrier.module.d.ts +10 -0
- package/dist/modules/carrier/carrier.module.js +39 -0
- package/dist/modules/carrier/carrier.module.spec.d.ts +1 -0
- package/dist/modules/carrier/carrier.module.spec.js +187 -0
- package/dist/modules/carrier/index.d.ts +3 -0
- package/dist/modules/carrier/index.js +3 -0
- package/dist/modules/carrier/mappings/index.d.ts +5 -0
- package/dist/modules/carrier/mappings/index.js +36 -0
- package/dist/modules/carrier/types/index.d.ts +42 -0
- package/dist/modules/carrier/types/index.js +2 -0
- package/dist/modules/diary/diary.module.d.ts +9 -0
- package/dist/modules/diary/diary.module.js +28 -0
- package/dist/modules/diary/index.d.ts +3 -0
- package/dist/modules/diary/index.js +3 -0
- package/dist/modules/diary/mappings/diary.mapper.d.ts +4 -0
- package/dist/modules/diary/mappings/diary.mapper.js +101 -0
- package/dist/modules/diary/mappings/diary.mapper.spec.d.ts +1 -0
- package/dist/modules/diary/mappings/diary.mapper.spec.js +18 -0
- package/dist/modules/diary/mappings/index.d.ts +1 -0
- package/dist/modules/diary/mappings/index.js +1 -0
- package/dist/modules/diary/types/diary.interface.d.ts +138 -0
- package/dist/modules/diary/types/diary.interface.js +24 -0
- package/dist/modules/diary/types/index.d.ts +1 -0
- package/dist/modules/diary/types/index.js +1 -0
- package/dist/modules/gift-voucher/gift-voucher.module.d.ts +12 -0
- package/dist/modules/gift-voucher/gift-voucher.module.js +55 -0
- package/dist/modules/gift-voucher/gift-voucher.module.spec.d.ts +1 -0
- package/dist/modules/gift-voucher/gift-voucher.module.spec.js +296 -0
- package/dist/modules/gift-voucher/index.d.ts +1 -0
- package/dist/modules/gift-voucher/index.js +1 -0
- package/dist/modules/gift-voucher/mappings/index.d.ts +6 -0
- package/dist/modules/gift-voucher/mappings/index.js +70 -0
- package/dist/modules/gift-voucher/mappings/index.spec.d.ts +1 -0
- package/dist/modules/gift-voucher/mappings/index.spec.js +21 -0
- package/dist/modules/gift-voucher/types/index.d.ts +92 -0
- package/dist/modules/gift-voucher/types/index.js +1 -0
- package/dist/modules/index.d.ts +8 -0
- package/dist/modules/index.js +8 -0
- package/dist/modules/point-of-sale/index.d.ts +3 -0
- package/dist/modules/point-of-sale/index.js +3 -0
- package/dist/modules/point-of-sale/mappings/index.d.ts +1 -0
- package/dist/modules/point-of-sale/mappings/index.js +1 -0
- package/dist/modules/point-of-sale/mappings/process-sale-order.mapper.d.ts +6 -0
- package/dist/modules/point-of-sale/mappings/process-sale-order.mapper.js +142 -0
- package/dist/modules/point-of-sale/point-of-sale.module.d.ts +43 -0
- package/dist/modules/point-of-sale/point-of-sale.module.js +64 -0
- package/dist/modules/point-of-sale/point-of-sale.module.spec.d.ts +1 -0
- package/dist/modules/point-of-sale/point-of-sale.module.spec.js +205 -0
- package/dist/modules/point-of-sale/types/cancel-existing-sales-order.interface.d.ts +13 -0
- package/dist/modules/point-of-sale/types/cancel-existing-sales-order.interface.js +1 -0
- package/dist/modules/point-of-sale/types/index.d.ts +124 -0
- package/dist/modules/point-of-sale/types/index.js +31 -0
- package/dist/modules/point-of-sale/types/modify-existing-sales-order.interface.d.ts +12 -0
- package/dist/modules/point-of-sale/types/modify-existing-sales-order.interface.js +1 -0
- package/dist/modules/point-of-sale/types/process-returns.interface.d.ts +17 -0
- package/dist/modules/point-of-sale/types/process-returns.interface.js +1 -0
- package/dist/modules/point-of-sale/types/process-sale-order.interface.d.ts +67 -0
- package/dist/modules/point-of-sale/types/process-sale-order.interface.js +1 -0
- package/dist/modules/shared/index.d.ts +1 -0
- package/dist/modules/shared/index.js +1 -0
- package/dist/modules/shared/types/index.d.ts +93 -0
- package/dist/modules/shared/types/index.js +53 -0
- package/dist/modules/stock-level-lookup/index.d.ts +3 -0
- package/dist/modules/stock-level-lookup/index.js +3 -0
- package/dist/modules/stock-level-lookup/mappings/index.d.ts +1 -0
- package/dist/modules/stock-level-lookup/mappings/index.js +1 -0
- package/dist/modules/stock-level-lookup/mappings/stock-level-lookup.mapper.d.ts +4 -0
- package/dist/modules/stock-level-lookup/mappings/stock-level-lookup.mapper.js +78 -0
- package/dist/modules/stock-level-lookup/mappings/stock-level-lookup.mapper.spec.d.ts +1 -0
- package/dist/modules/stock-level-lookup/mappings/stock-level-lookup.mapper.spec.js +57 -0
- package/dist/modules/stock-level-lookup/stock-level-lookup.module.d.ts +10 -0
- package/dist/modules/stock-level-lookup/stock-level-lookup.module.js +39 -0
- package/dist/modules/stock-level-lookup/stock-level-lookup.module.spec.d.ts +1 -0
- package/dist/modules/stock-level-lookup/stock-level-lookup.module.spec.js +317 -0
- package/dist/modules/stock-level-lookup/types/index.d.ts +1 -0
- package/dist/modules/stock-level-lookup/types/index.js +1 -0
- package/dist/modules/stock-level-lookup/types/stock-level-lookup.interface.d.ts +162 -0
- package/dist/modules/stock-level-lookup/types/stock-level-lookup.interface.js +61 -0
- package/dist/modules/stock-lookup/index.d.ts +3 -0
- package/dist/modules/stock-lookup/index.js +3 -0
- package/dist/modules/stock-lookup/mappings/index.d.ts +1 -0
- package/dist/modules/stock-lookup/mappings/index.js +1 -0
- package/dist/modules/stock-lookup/mappings/stock-lookup.mapper.d.ts +22 -0
- package/dist/modules/stock-lookup/mappings/stock-lookup.mapper.js +156 -0
- package/dist/modules/stock-lookup/mappings/stock-lookup.mapper.spec.d.ts +1 -0
- package/dist/modules/stock-lookup/mappings/stock-lookup.mapper.spec.js +92 -0
- package/dist/modules/stock-lookup/stock-lookup.module.d.ts +65 -0
- package/dist/modules/stock-lookup/stock-lookup.module.js +141 -0
- package/dist/modules/stock-lookup/stock-lookup.module.spec.d.ts +1 -0
- package/dist/modules/stock-lookup/stock-lookup.module.spec.js +419 -0
- package/dist/modules/stock-lookup/types/index.d.ts +1 -0
- package/dist/modules/stock-lookup/types/index.js +1 -0
- package/dist/modules/stock-lookup/types/stock-lookup.interface.d.ts +242 -0
- package/dist/modules/stock-lookup/types/stock-lookup.interface.js +82 -0
- package/dist/modules/stock-lookup/types/stock-lookup.interface.spec.d.ts +1 -0
- package/dist/modules/stock-lookup/types/stock-lookup.interface.spec.js +76 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +6 -0
- package/package.json +39 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { ApiHelper, checkParamLimits, Utils } from ".";
|
|
3
|
+
import { ServiceAlias } from "../modules/shared/types";
|
|
4
|
+
// Increase timeout due to Harmony API having long response time
|
|
5
|
+
jest.setTimeout(999999);
|
|
6
|
+
jest.mock("axios");
|
|
7
|
+
const mAxios = axios;
|
|
8
|
+
describe("ApiHelper", () => {
|
|
9
|
+
test("ApiHelper.sendSoapRequest", async () => {
|
|
10
|
+
const endpoint = "/StockLookupService/StockLookupService";
|
|
11
|
+
const sessionId = "test-session-id";
|
|
12
|
+
const requestBody = `
|
|
13
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stk="http://stk.ws.fbsaust.com.au">
|
|
14
|
+
<S:Header>
|
|
15
|
+
<stk:SessionId>${sessionId}</stk:SessionId>
|
|
16
|
+
</S:Header>
|
|
17
|
+
<S:Body>
|
|
18
|
+
<stk:StockClassificationLookup>
|
|
19
|
+
<SearchType>STK00</SearchType>
|
|
20
|
+
</stk:StockClassificationLookup>
|
|
21
|
+
</S:Body>
|
|
22
|
+
</S:Envelope>
|
|
23
|
+
`;
|
|
24
|
+
mAxios.post.mockResolvedValueOnce({
|
|
25
|
+
data: `
|
|
26
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
|
27
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
28
|
+
<S:Body>
|
|
29
|
+
<ns2:StockClassificationLookupResponse xmlns:ns2="http://stk.ws.fbsaust.com.au">
|
|
30
|
+
<StockClassification>
|
|
31
|
+
<code>BACK</code>
|
|
32
|
+
<description>TEST</description>
|
|
33
|
+
</StockClassification>
|
|
34
|
+
</ns2:StockClassificationLookupResponse>
|
|
35
|
+
</S:Body>
|
|
36
|
+
</S:Envelope>
|
|
37
|
+
`,
|
|
38
|
+
});
|
|
39
|
+
const response = await ApiHelper.sendSoapRequest(endpoint, mAxios, requestBody);
|
|
40
|
+
expect(response["ns2:StockClassificationLookupResponse"][0]
|
|
41
|
+
.StockClassification).toEqual([
|
|
42
|
+
{
|
|
43
|
+
code: ["BACK"],
|
|
44
|
+
description: ["TEST"],
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
test("createServiceRequestBody should return correct SOAP request body", () => {
|
|
49
|
+
const expected = `
|
|
50
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stk="http://stk.ws.fbsaust.com.au">
|
|
51
|
+
<S:Header>
|
|
52
|
+
<stk:SessionId>ABC</stk:SessionId>
|
|
53
|
+
</S:Header>
|
|
54
|
+
<S:Body>
|
|
55
|
+
<stk:StockClassificationLookup>
|
|
56
|
+
<SearchType>STK99</SearchType>
|
|
57
|
+
</stk:StockClassificationLookup>
|
|
58
|
+
</S:Body>
|
|
59
|
+
</S:Envelope>
|
|
60
|
+
`;
|
|
61
|
+
const actual = ApiHelper.createServiceRequestBody("StockClassificationLookup", "ABC", ServiceAlias.STOCK_LOOKUP, "<SearchType>STK99</SearchType>");
|
|
62
|
+
expect(Utils.removeEmptySpaces(actual)).toEqual(Utils.removeEmptySpaces(expected));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("Other functions", () => {
|
|
66
|
+
test("checkParamLimit", () => {
|
|
67
|
+
const params = {
|
|
68
|
+
param1: 123,
|
|
69
|
+
param2: "abc",
|
|
70
|
+
param3: true,
|
|
71
|
+
};
|
|
72
|
+
// Matching param limit
|
|
73
|
+
const matchedParamLimit = {
|
|
74
|
+
param1: 3,
|
|
75
|
+
param2: 3,
|
|
76
|
+
param3: (val) => typeof val === "boolean" && val === true,
|
|
77
|
+
};
|
|
78
|
+
expect(checkParamLimits(params, matchedParamLimit)).toBe(true);
|
|
79
|
+
// Exceeded param limit
|
|
80
|
+
const exceededParamLimit = {
|
|
81
|
+
param1: 3,
|
|
82
|
+
param2: 2,
|
|
83
|
+
param3: (val) => typeof val === "boolean" && val === true,
|
|
84
|
+
};
|
|
85
|
+
expect(() => checkParamLimits(params, exceededParamLimit)).toThrow(`Provided value for "param2" exceeds limit of 2`);
|
|
86
|
+
// Limit using function return false
|
|
87
|
+
const falseParamLimit = {
|
|
88
|
+
param1: 3,
|
|
89
|
+
param2: 3,
|
|
90
|
+
param3: (val) => val === false,
|
|
91
|
+
};
|
|
92
|
+
expect(() => checkParamLimits(params, falseParamLimit)).toThrow(`Invalid value for parameter "param3": true`);
|
|
93
|
+
});
|
|
94
|
+
test("checkParamLimit with enum", () => {
|
|
95
|
+
let TestEnum;
|
|
96
|
+
(function (TestEnum) {
|
|
97
|
+
TestEnum[TestEnum["A"] = 0] = "A";
|
|
98
|
+
TestEnum[TestEnum["B"] = 1] = "B";
|
|
99
|
+
})(TestEnum || (TestEnum = {}));
|
|
100
|
+
const params = {
|
|
101
|
+
param1: TestEnum.A,
|
|
102
|
+
};
|
|
103
|
+
const wrongParams = {
|
|
104
|
+
param1: "C",
|
|
105
|
+
};
|
|
106
|
+
// Matching param limit
|
|
107
|
+
const matchedParamLimit = {
|
|
108
|
+
param1: (val) => Object.values(TestEnum).includes(val),
|
|
109
|
+
};
|
|
110
|
+
expect(checkParamLimits(params, matchedParamLimit)).toBe(true);
|
|
111
|
+
expect(() => checkParamLimits(wrongParams, matchedParamLimit)).toThrow(`Invalid value for parameter "param1": C`);
|
|
112
|
+
});
|
|
113
|
+
test("checkParamLimit with no limit", () => {
|
|
114
|
+
const params = {
|
|
115
|
+
param1: "asdf",
|
|
116
|
+
};
|
|
117
|
+
// Matching param limit
|
|
118
|
+
const matchedParamLimit = {};
|
|
119
|
+
expect(checkParamLimits(params, matchedParamLimit)).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("parseRequestError", () => {
|
|
123
|
+
it("should handle error correctly and return the error string", async () => {
|
|
124
|
+
const errResp = `
|
|
125
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
|
126
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
127
|
+
<S:Body>
|
|
128
|
+
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
|
|
129
|
+
<faultcode>S:Server</faultcode>
|
|
130
|
+
<faultstring>Failed Test Request</faultstring>
|
|
131
|
+
<detail>
|
|
132
|
+
<ns2:ServiceFault xmlns:ns2="http://pos.ws.fbsaust.com.au">
|
|
133
|
+
<errorDetails>[204-104-2408071537-0000851206] (Invalid value)</errorDetails>
|
|
134
|
+
</ns2:ServiceFault>
|
|
135
|
+
</detail>
|
|
136
|
+
</S:Fault>
|
|
137
|
+
</S:Body>
|
|
138
|
+
</S:Envelope>
|
|
139
|
+
`;
|
|
140
|
+
const expected = {
|
|
141
|
+
code: "S:Server",
|
|
142
|
+
name: "Failed Test Request",
|
|
143
|
+
message: "[204-104-2408071537-0000851206] (Invalid value)",
|
|
144
|
+
};
|
|
145
|
+
expect(await ApiHelper.parseHarmonyErrorResponse(errResp)).toEqual(expected);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { OptionalStockRequestBody, } from "../modules/shared/types";
|
|
2
|
+
export function mapOptionalStockQueryParams(src) {
|
|
3
|
+
return new OptionalStockRequestBody({
|
|
4
|
+
StockCategoryFr: src?.stockCategoryFr ?? "",
|
|
5
|
+
StockCategoryTo: src?.stockCategoryTo ?? "",
|
|
6
|
+
StockClassFr1: src?.stockClassFr1 ?? "",
|
|
7
|
+
StockClassTo1: src?.stockClassTo1 ?? "",
|
|
8
|
+
StockClassFr2: src?.stockClassFr2 ?? "",
|
|
9
|
+
StockClassTo2: src?.stockClassTo2 ?? "",
|
|
10
|
+
StockClassFr3: src?.stockClassFr3 ?? "",
|
|
11
|
+
StockClassTo3: src?.stockClassTo3 ?? "",
|
|
12
|
+
StockExtraClassFr1: src?.stockExtraClassFr1 ?? "",
|
|
13
|
+
StockExtraClassTo1: src?.stockExtraClassTo1 ?? "",
|
|
14
|
+
StockExtraClassFr2: src?.stockExtraClassFr2 ?? "",
|
|
15
|
+
StockExtraClassTo2: src?.stockExtraClassTo2 ?? "",
|
|
16
|
+
StockExtraClassFr3: src?.stockExtraClassFr3 ?? "",
|
|
17
|
+
StockExtraClassTo3: src?.stockExtraClassTo3 ?? "",
|
|
18
|
+
StockExtraClassFr4: src?.stockExtraClassFr4 ?? "",
|
|
19
|
+
StockExtraClassTo4: src?.stockExtraClassTo4 ?? "",
|
|
20
|
+
StockExtraClassFr5: src?.stockExtraClassFr5 ?? "",
|
|
21
|
+
StockExtraClassTo5: src?.stockExtraClassTo5 ?? "",
|
|
22
|
+
SysModTimeFr: src?.sysModTimeFr ?? "",
|
|
23
|
+
SysModTimeTo: src?.sysModTimeTo ?? "",
|
|
24
|
+
AlternateNamekeyFr: src?.alternateNamekeyFr ?? "",
|
|
25
|
+
AlternateNamekeyTo: src?.alternateNamekeyTo ?? "",
|
|
26
|
+
StockActive: src?.stockActive?.map(String)?.join("") ?? "",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export declare abstract class Utils {
|
|
2
|
+
/**
|
|
3
|
+
* Check if an object is an empty array or not an array
|
|
4
|
+
*
|
|
5
|
+
* @static
|
|
6
|
+
* @param {*} obj object to check
|
|
7
|
+
* @returns {boolean} true if object is an empty array
|
|
8
|
+
*/
|
|
9
|
+
static isEmptyArray(obj: any): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Check if an object is a non-empty array.
|
|
12
|
+
*
|
|
13
|
+
* @static
|
|
14
|
+
* @param {*} obj object to check
|
|
15
|
+
* @returns {boolean} true if object is a non-empty array
|
|
16
|
+
*/
|
|
17
|
+
static nonEmptyArray(obj: any): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Chcek if an object is an empty string or not a string
|
|
20
|
+
*
|
|
21
|
+
* @static
|
|
22
|
+
* @param {*} obj object to check
|
|
23
|
+
* @returns {boolean} true if object is an empty string
|
|
24
|
+
*/
|
|
25
|
+
static isEmptyString(obj: any): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an object is a non-empty string.
|
|
28
|
+
*
|
|
29
|
+
* @static
|
|
30
|
+
* @param {*} obj object to check
|
|
31
|
+
* @returns {boolean} true if object is a non-empty string
|
|
32
|
+
*/
|
|
33
|
+
static nonEmptyString(obj: any): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Convert a value to a number.
|
|
36
|
+
*
|
|
37
|
+
* @static
|
|
38
|
+
* @param {*} obj object to convert to a number
|
|
39
|
+
* @returns {number} the converted number
|
|
40
|
+
*/
|
|
41
|
+
static toNumber(obj: any): number;
|
|
42
|
+
static checkMaxStringLength(str: any, length: number, propName?: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Remove all empty spaces within a string, including new lines and tabs...
|
|
45
|
+
*
|
|
46
|
+
* @static
|
|
47
|
+
* @param {string} str to remove empty spaces from
|
|
48
|
+
* @returns {string} string with empty spaces removed
|
|
49
|
+
*/
|
|
50
|
+
static removeEmptySpaces(str: string): string;
|
|
51
|
+
static isXmlEqualJson(xml: string, json: any): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Recursively convert an object into xml format
|
|
54
|
+
*
|
|
55
|
+
* @static
|
|
56
|
+
* @param {*} obj
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
static toXml(obj: object, parent?: string): string;
|
|
60
|
+
static deleteUndefinedProps(obj: any): any;
|
|
61
|
+
static deleteEmptyProps(obj: any): any;
|
|
62
|
+
/**
|
|
63
|
+
* Returns first element of an array.
|
|
64
|
+
* Responses extracted from XML have all properties in the format of arrays of 1 element. This is used to extract value out of those properties
|
|
65
|
+
*
|
|
66
|
+
* @static
|
|
67
|
+
* @param {*} prop property array to extract
|
|
68
|
+
* @returns {*} first value in the array
|
|
69
|
+
*/
|
|
70
|
+
static getFirst(prop: any): string;
|
|
71
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { promisify } from "util";
|
|
2
|
+
import { parseString } from "xml2js";
|
|
3
|
+
const parseXml = promisify(parseString);
|
|
4
|
+
export class Utils {
|
|
5
|
+
/**
|
|
6
|
+
* Check if an object is an empty array or not an array
|
|
7
|
+
*
|
|
8
|
+
* @static
|
|
9
|
+
* @param {*} obj object to check
|
|
10
|
+
* @returns {boolean} true if object is an empty array
|
|
11
|
+
*/
|
|
12
|
+
static isEmptyArray(obj) {
|
|
13
|
+
return !Utils.nonEmptyArray(obj);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if an object is a non-empty array.
|
|
17
|
+
*
|
|
18
|
+
* @static
|
|
19
|
+
* @param {*} obj object to check
|
|
20
|
+
* @returns {boolean} true if object is a non-empty array
|
|
21
|
+
*/
|
|
22
|
+
static nonEmptyArray(obj) {
|
|
23
|
+
return Array.isArray(obj) && obj.length > 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Chcek if an object is an empty string or not a string
|
|
27
|
+
*
|
|
28
|
+
* @static
|
|
29
|
+
* @param {*} obj object to check
|
|
30
|
+
* @returns {boolean} true if object is an empty string
|
|
31
|
+
*/
|
|
32
|
+
static isEmptyString(obj) {
|
|
33
|
+
return !Utils.nonEmptyString(obj);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if an object is a non-empty string.
|
|
37
|
+
*
|
|
38
|
+
* @static
|
|
39
|
+
* @param {*} obj object to check
|
|
40
|
+
* @returns {boolean} true if object is a non-empty string
|
|
41
|
+
*/
|
|
42
|
+
static nonEmptyString(obj) {
|
|
43
|
+
return typeof obj === "string" && obj.trim() !== "";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert a value to a number.
|
|
47
|
+
*
|
|
48
|
+
* @static
|
|
49
|
+
* @param {*} obj object to convert to a number
|
|
50
|
+
* @returns {number} the converted number
|
|
51
|
+
*/
|
|
52
|
+
static toNumber(obj) {
|
|
53
|
+
const num = Number(obj);
|
|
54
|
+
if (isNaN(num))
|
|
55
|
+
throw new Error(`Provided value [${obj}] is not a number.`);
|
|
56
|
+
return num;
|
|
57
|
+
}
|
|
58
|
+
static checkMaxStringLength(str, length, propName = "property") {
|
|
59
|
+
if (!(typeof str === "string" && str.length <= length))
|
|
60
|
+
throw new Error(`Provided ${propName} exceeds maximum length of ${length} characters.`);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Remove all empty spaces within a string, including new lines and tabs...
|
|
65
|
+
*
|
|
66
|
+
* @static
|
|
67
|
+
* @param {string} str to remove empty spaces from
|
|
68
|
+
* @returns {string} string with empty spaces removed
|
|
69
|
+
*/
|
|
70
|
+
static removeEmptySpaces(str) {
|
|
71
|
+
return str.replace(/\s/g, "");
|
|
72
|
+
}
|
|
73
|
+
static async isXmlEqualJson(xml, json) {
|
|
74
|
+
const result = (await parseXml(`<result>${xml}</result>`))["result"];
|
|
75
|
+
// Test that each key value pair matches the expected
|
|
76
|
+
for (const k in result) {
|
|
77
|
+
const val = result[k][0];
|
|
78
|
+
if (json[k] !== val)
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Recursively convert an object into xml format
|
|
85
|
+
*
|
|
86
|
+
* @static
|
|
87
|
+
* @param {*} obj
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
static toXml(obj, parent) {
|
|
91
|
+
if (obj == null)
|
|
92
|
+
return "";
|
|
93
|
+
let str = "";
|
|
94
|
+
const open = parent ? `<${parent}>` : "";
|
|
95
|
+
const close = parent ? `</${parent}>` : "";
|
|
96
|
+
if (Array.isArray(obj)) {
|
|
97
|
+
str += obj.map((o) => open + Utils.toXml(o) + close).join("");
|
|
98
|
+
}
|
|
99
|
+
else if (typeof obj === "object") {
|
|
100
|
+
let valStr = "";
|
|
101
|
+
for (const k in obj) {
|
|
102
|
+
const val = obj[k];
|
|
103
|
+
if (!(val == null)) {
|
|
104
|
+
valStr += `${Utils.toXml(val, k)}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
str += open + valStr + close;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
str += open + obj + close;
|
|
111
|
+
}
|
|
112
|
+
return str;
|
|
113
|
+
}
|
|
114
|
+
static deleteUndefinedProps(obj) {
|
|
115
|
+
Object.keys(obj).forEach((key) => obj[key] === undefined && delete obj[key]);
|
|
116
|
+
return obj;
|
|
117
|
+
}
|
|
118
|
+
static deleteEmptyProps(obj) {
|
|
119
|
+
Object.keys(obj).forEach((key) => [undefined, null, ""].includes(obj[key]) && delete obj[key]);
|
|
120
|
+
return obj;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Returns first element of an array.
|
|
124
|
+
* Responses extracted from XML have all properties in the format of arrays of 1 element. This is used to extract value out of those properties
|
|
125
|
+
*
|
|
126
|
+
* @static
|
|
127
|
+
* @param {*} prop property array to extract
|
|
128
|
+
* @returns {*} first value in the array
|
|
129
|
+
*/
|
|
130
|
+
static getFirst(prop) {
|
|
131
|
+
return (prop ?? [])[0];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Utils } from "./utils";
|
|
2
|
+
describe("Utils", () => {
|
|
3
|
+
test("isEmptyArray", () => {
|
|
4
|
+
expect.assertions(7);
|
|
5
|
+
const nonEmpty = [1, 2, 3];
|
|
6
|
+
const nonEmptyStr = ["1", "2", "3"];
|
|
7
|
+
expect(Utils.isEmptyArray(nonEmpty)).toBe(false);
|
|
8
|
+
expect(Utils.isEmptyArray(nonEmptyStr)).toBe(false);
|
|
9
|
+
expect(Utils.isEmptyArray([])).toBe(true);
|
|
10
|
+
expect(Utils.isEmptyArray("")).toBe(true);
|
|
11
|
+
expect(Utils.isEmptyArray("asadf")).toBe(true);
|
|
12
|
+
expect(Utils.isEmptyArray(null)).toBe(true);
|
|
13
|
+
expect(Utils.isEmptyArray(undefined)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
test("isEmptyString", () => {
|
|
16
|
+
expect.assertions(6);
|
|
17
|
+
const nonEmpty = "test";
|
|
18
|
+
expect(Utils.isEmptyString(nonEmpty)).toBe(false);
|
|
19
|
+
expect(Utils.isEmptyString([])).toBe(true);
|
|
20
|
+
expect(Utils.isEmptyString(["1", "2", "3"])).toBe(true);
|
|
21
|
+
expect(Utils.isEmptyString("")).toBe(true);
|
|
22
|
+
expect(Utils.isEmptyString(null)).toBe(true);
|
|
23
|
+
expect(Utils.isEmptyString(undefined)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
test("toNumber", () => {
|
|
26
|
+
const invalidValues = [undefined, "abc", "123abc", {}, "*&^*"];
|
|
27
|
+
for (const i of invalidValues) {
|
|
28
|
+
expect(() => Utils.toNumber(i)).toThrow(`Provided value [${i}] is not a number.`);
|
|
29
|
+
}
|
|
30
|
+
expect(Utils.toNumber("123")).toBe(123);
|
|
31
|
+
expect(Utils.toNumber("123.45")).toBe(123.45);
|
|
32
|
+
expect(Utils.toNumber(true)).toBe(1);
|
|
33
|
+
expect(Utils.toNumber(false)).toBe(0);
|
|
34
|
+
expect(Utils.toNumber("")).toBe(0);
|
|
35
|
+
expect(Utils.toNumber(null)).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
test("toXml should convert to XML string successfully", () => {
|
|
38
|
+
const obj = {
|
|
39
|
+
name: "John Doe",
|
|
40
|
+
age: 30,
|
|
41
|
+
city: "New York",
|
|
42
|
+
hobbies: ["reading", "painting"],
|
|
43
|
+
address: {
|
|
44
|
+
street: "123 Main St",
|
|
45
|
+
city: "New York",
|
|
46
|
+
state: "NY",
|
|
47
|
+
zip: "10001",
|
|
48
|
+
},
|
|
49
|
+
count: 0,
|
|
50
|
+
nullable: null,
|
|
51
|
+
};
|
|
52
|
+
const expectedXml = `
|
|
53
|
+
<name>John Doe</name>
|
|
54
|
+
<age>30</age>
|
|
55
|
+
<city>New York</city>
|
|
56
|
+
<hobbies>reading</hobbies>
|
|
57
|
+
<hobbies>painting</hobbies>
|
|
58
|
+
<address>
|
|
59
|
+
<street>123 Main St</street>
|
|
60
|
+
<city>New York</city>
|
|
61
|
+
<state>NY</state>
|
|
62
|
+
<zip>10001</zip>
|
|
63
|
+
</address>
|
|
64
|
+
<count>0</count>
|
|
65
|
+
`;
|
|
66
|
+
expect(Utils.removeEmptySpaces(Utils.toXml(obj))).toEqual(Utils.removeEmptySpaces(expectedXml));
|
|
67
|
+
});
|
|
68
|
+
test("deleteUndefinedProps() should remove undefined properties", () => {
|
|
69
|
+
const obj = {
|
|
70
|
+
"1": true,
|
|
71
|
+
"2": "test",
|
|
72
|
+
"3": undefined,
|
|
73
|
+
"4": null,
|
|
74
|
+
};
|
|
75
|
+
Utils.deleteUndefinedProps(obj);
|
|
76
|
+
expect(obj).toEqual({
|
|
77
|
+
"1": true,
|
|
78
|
+
"2": "test",
|
|
79
|
+
"4": null,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
test("deleteEmptyProps() should remove all empty properties", () => {
|
|
83
|
+
const obj = {
|
|
84
|
+
"1": true,
|
|
85
|
+
"2": "test",
|
|
86
|
+
"3": undefined,
|
|
87
|
+
"4": null,
|
|
88
|
+
"5": "",
|
|
89
|
+
};
|
|
90
|
+
Utils.deleteEmptyProps(obj);
|
|
91
|
+
expect(obj).toEqual({
|
|
92
|
+
"1": true,
|
|
93
|
+
"2": "test",
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AuthenticationError } from "../../errors";
|
|
2
|
+
import { ApiHelper } from "../../helpers";
|
|
3
|
+
export class AuthModule {
|
|
4
|
+
axiosInstance;
|
|
5
|
+
constructor(axiosInstance) {
|
|
6
|
+
this.axiosInstance = axiosInstance;
|
|
7
|
+
}
|
|
8
|
+
async authenticate(authToken) {
|
|
9
|
+
// @todo: TBC whether using `xmlbuilder` or template string
|
|
10
|
+
const authHeader = `
|
|
11
|
+
<ns2:AuthenticationToken xmlns:ns2="http://auth.ws.fbsaust.com.au" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
12
|
+
<username>${authToken.username}</username>
|
|
13
|
+
<method>${authToken.method}</method>
|
|
14
|
+
<password>${authToken.password}</password>
|
|
15
|
+
<timeout>${authToken.timeout}${authToken.timeoutUnit}</timeout>
|
|
16
|
+
</ns2:AuthenticationToken>
|
|
17
|
+
`;
|
|
18
|
+
const loginBody = '<ns2:Login xmlns:ns2="http://auth.ws.fbsaust.com.au"/>';
|
|
19
|
+
const soapEnvelope = `
|
|
20
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
21
|
+
<S:Header>${authHeader}</S:Header>
|
|
22
|
+
<S:Body>${loginBody}</S:Body>
|
|
23
|
+
</S:Envelope>
|
|
24
|
+
`;
|
|
25
|
+
try {
|
|
26
|
+
const response = await ApiHelper.sendSoapRequest("/AuthenticationService/AuthenticationService", this.axiosInstance, soapEnvelope);
|
|
27
|
+
const token = response["ns2:LoginResponse"][0]["token"][0];
|
|
28
|
+
if (!token) {
|
|
29
|
+
throw new AuthenticationError("No token received in the authentication response");
|
|
30
|
+
}
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof AuthenticationError) {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new AuthenticationError(`An unexpected error occurred: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { ApiHelper, Utils } from "../../helpers";
|
|
3
|
+
import { AuthModule } from "./auth.module";
|
|
4
|
+
jest.mock("axios");
|
|
5
|
+
const mAxios = axios;
|
|
6
|
+
const sessionId = ""; // process.env.SESSION_ID as string;
|
|
7
|
+
describe("AuthModule", () => {
|
|
8
|
+
let authModule;
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
authModule = new AuthModule(mAxios);
|
|
11
|
+
});
|
|
12
|
+
test("authenticate successfully", async () => {
|
|
13
|
+
const mockedSendApiRequest = jest.spyOn(ApiHelper, "sendSoapRequest");
|
|
14
|
+
const authToken = {
|
|
15
|
+
username: "test-username",
|
|
16
|
+
password: "test-password",
|
|
17
|
+
method: "Basic",
|
|
18
|
+
timeout: 365,
|
|
19
|
+
timeoutUnit: "day",
|
|
20
|
+
};
|
|
21
|
+
const mockedToken = "CI03GWHZzXO0VJQ6JSLJoPwWijXTIPilphKvlLhLHcjDQ3yrr6eJeTK6wxDb8XZb";
|
|
22
|
+
const mockedResponse = {
|
|
23
|
+
data: `
|
|
24
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
|
25
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
26
|
+
<S:Body>
|
|
27
|
+
<ns2:LoginResponse xmlns:ns2="http://auth.ws.fbsaust.com.au">
|
|
28
|
+
<token>${mockedToken}</token>
|
|
29
|
+
</ns2:LoginResponse>
|
|
30
|
+
</S:Body>
|
|
31
|
+
</S:Envelope>
|
|
32
|
+
`,
|
|
33
|
+
};
|
|
34
|
+
mAxios.post.mockResolvedValueOnce(mockedResponse);
|
|
35
|
+
const token = await authModule.authenticate(authToken);
|
|
36
|
+
expect(mockedSendApiRequest).toHaveBeenCalled();
|
|
37
|
+
expect(mAxios.post).toHaveBeenCalled();
|
|
38
|
+
expect(Utils.removeEmptySpaces(mAxios.post.mock.calls[0][1])).toEqual(Utils.removeEmptySpaces(`
|
|
39
|
+
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
40
|
+
<S:Header>
|
|
41
|
+
<ns2:AuthenticationToken xmlns:ns2="http://auth.ws.fbsaust.com.au" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
|
42
|
+
<username>${authToken.username}</username>
|
|
43
|
+
<method>${authToken.method}</method>
|
|
44
|
+
<password>${authToken.password}</password>
|
|
45
|
+
<timeout>${authToken.timeout}${authToken.timeoutUnit}</timeout>
|
|
46
|
+
</ns2:AuthenticationToken>
|
|
47
|
+
</S:Header>
|
|
48
|
+
<S:Body>
|
|
49
|
+
<ns2:Login xmlns:ns2="http://auth.ws.fbsaust.com.au"/>
|
|
50
|
+
</S:Body>
|
|
51
|
+
</S:Envelope>
|
|
52
|
+
`));
|
|
53
|
+
expect(token).toEqual(mockedToken);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
import { Carrier, CarrierShipment, GetCarrierShipmentByTimeQueryParams } from "./types";
|
|
3
|
+
export declare class CarrierModule {
|
|
4
|
+
private axiosInstance;
|
|
5
|
+
private readonly SERVICE_URL;
|
|
6
|
+
constructor(axiosInstance: AxiosInstance);
|
|
7
|
+
getCarrier(sessionId: string): Promise<Carrier[]>;
|
|
8
|
+
getCarrierShipmentByOrderNumber(orderNo: string, sessionId: string): Promise<CarrierShipment[]>;
|
|
9
|
+
getCarrierShipmentBySysTime(params: GetCarrierShipmentByTimeQueryParams, sessionId: string): Promise<CarrierShipment[]>;
|
|
10
|
+
}
|