@transai/connector-runner-mkg 0.2.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.
Files changed (44) hide show
  1. package/.eslintrc.json +30 -0
  2. package/CHANGELOG.md +24 -0
  3. package/README.md +11 -0
  4. package/jest.config.ts +10 -0
  5. package/package.json +16 -0
  6. package/project.json +29 -0
  7. package/src/index.ts +1 -0
  8. package/src/lib/actions-handler.spec.ts +177 -0
  9. package/src/lib/actions-handler.ts +143 -0
  10. package/src/lib/connector-runner-mkg.spec.ts +219 -0
  11. package/src/lib/connector-runner-mkg.ts +155 -0
  12. package/src/lib/extractor.service.spec.ts +123 -0
  13. package/src/lib/extractor.service.ts +125 -0
  14. package/src/lib/tables/_all.ts +60 -0
  15. package/src/lib/tables/artg.ts +12 -0
  16. package/src/lib/tables/arti.ts +99 -0
  17. package/src/lib/tables/base/action.ts +70 -0
  18. package/src/lib/tables/base/table.ts +57 -0
  19. package/src/lib/tables/bwrg.ts +13 -0
  20. package/src/lib/tables/bwrk.ts +60 -0
  21. package/src/lib/tables/clch.ts +42 -0
  22. package/src/lib/tables/debi.ts +62 -0
  23. package/src/lib/tables/magl.ts +15 -0
  24. package/src/lib/tables/magz.ts +14 -0
  25. package/src/lib/tables/medw.ts +97 -0
  26. package/src/lib/tables/parl.ts +24 -0
  27. package/src/lib/tables/plnb.ts +46 -0
  28. package/src/lib/tables/prbv.ts +31 -0
  29. package/src/lib/tables/prdh.ts +46 -0
  30. package/src/lib/tables/prdr.ts +25 -0
  31. package/src/lib/tables/prmv.ts +31 -0
  32. package/src/lib/tables/rela.ts +40 -0
  33. package/src/lib/tables/rgrs.ts +68 -0
  34. package/src/lib/tables/rsrc.ts +23 -0
  35. package/src/lib/tables/rsrd.ts +98 -0
  36. package/src/lib/tables/rsrg.ts +6 -0
  37. package/src/lib/tables/stlh.ts +24 -0
  38. package/src/lib/tables/stlm.ts +80 -0
  39. package/src/lib/tables/stlr.ts +60 -0
  40. package/src/lib/tables/vrdg.ts +27 -0
  41. package/src/lib/types.ts +45 -0
  42. package/tsconfig.json +22 -0
  43. package/tsconfig.lib.json +10 -0
  44. package/tsconfig.spec.json +14 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "extends": ["../../.eslintrc.json"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {}
8
+ },
9
+ {
10
+ "files": ["*.ts", "*.tsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.js", "*.jsx"],
15
+ "rules": {}
16
+ },
17
+ {
18
+ "files": ["*.json"],
19
+ "parser": "jsonc-eslint-parser",
20
+ "rules": {
21
+ "@nx/dependency-checks": [
22
+ "error",
23
+ {
24
+ "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"]
25
+ }
26
+ ]
27
+ }
28
+ }
29
+ ]
30
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ ## 0.2.0 (2025-11-19)
2
+
3
+ ### 🚀 Features
4
+
5
+ - fix connector packages publish ([517d2fd2](https://github.com/xip-online-applications/xod-core/commit/517d2fd2))
6
+ - **XODO-1108:** added actions for MKG tables ([#877](https://github.com/xip-online-applications/xod-core/pull/877))
7
+ - add custom cron trigger functionality and related interfaces ([b859c50f](https://github.com/xip-online-applications/xod-core/commit/b859c50f))
8
+ - **XODO-1125:** fixes ([b625be91](https://github.com/xip-online-applications/xod-core/commit/b625be91))
9
+ - ability to send statistics to logs for now ([#863](https://github.com/xip-online-applications/xod-core/pull/863))
10
+ - **XODO-1108:** added MKG ERP connector ([#840](https://github.com/xip-online-applications/xod-core/pull/840))
11
+
12
+ ### 🩹 Fixes
13
+
14
+ - correct timestamp expectation in ExtractorService tests and add CustomCronTriggerRecord interface ([e3f53d35](https://github.com/xip-online-applications/xod-core/commit/e3f53d35))
15
+
16
+ ### 🧱 Updated Dependencies
17
+
18
+ - Updated @transai/connector-runtime-sdk to 0.1.0
19
+
20
+ ### ❤️ Thank You
21
+
22
+ - Copilot @Copilot
23
+ - Rene Heijdens @H31nz3l
24
+ - Youri Lefers @yourilefers
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # connector-runner-mkg
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build connector-runner-mkg` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test connector-runner-mkg` to execute the unit tests via [Jest](https://jestjs.io).
package/jest.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ export default {
2
+ displayName: 'connector-runner-mkg',
3
+ preset: '../../jest.preset.js',
4
+ testEnvironment: 'node',
5
+ transform: {
6
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
+ },
8
+ moduleFileExtensions: ['ts', 'js', 'html'],
9
+ coverageDirectory: '../../coverage/libs/connector-runner-mkg',
10
+ };
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@transai/connector-runner-mkg",
3
+ "version": "0.2.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "license": "LGPL-3.0-or-later",
8
+ "author": {
9
+ "name": "transAI",
10
+ "email": "samen@transai.com",
11
+ "url": "https://transai.com"
12
+ },
13
+ "type": "commonjs",
14
+ "main": "./index.js",
15
+ "typings": "./index.d.ts"
16
+ }
package/project.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "connector-runner-mkg",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/connector-runner-mkg/src",
5
+ "projectType": "library",
6
+ "tags": [
7
+ "connector:runner",
8
+ "connector:runner-sdk",
9
+ "connector:mkg",
10
+ "connector:source",
11
+ "provider:@transai"
12
+ ],
13
+ "targets": {
14
+ "build": {
15
+ "executor": "@transai/tools:connector",
16
+ "outputs": ["{options.outputPath}"]
17
+ },
18
+ "lint": {
19
+ "executor": "@nx/eslint:lint"
20
+ },
21
+ "test": {
22
+ "executor": "@nx/jest:jest",
23
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
24
+ "options": {
25
+ "jestConfig": "libs/connector-runner-mkg/jest.config.ts"
26
+ }
27
+ }
28
+ }
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib/connector-runner-mkg';
@@ -0,0 +1,177 @@
1
+ import {
2
+ ConnectorSDKInterface,
3
+ HttpClientSDKInterface,
4
+ } from '@transai/connector-runtime-sdk';
5
+ import { ActionInterface, XodJobType } from '@xip-online-data/types';
6
+
7
+ import { ActionsHandler } from './actions-handler';
8
+
9
+ describe('ActionsHandler', () => {
10
+ let handler: ActionsHandler;
11
+
12
+ const sdkMock = {
13
+ logger: {
14
+ warn: jest.fn(),
15
+ info: jest.fn(),
16
+ debug: jest.fn(),
17
+ error: jest.fn(),
18
+ verbose: jest.fn(),
19
+ },
20
+ receiver: {
21
+ responses: {
22
+ created: jest.fn().mockReturnValue(
23
+ jest.fn().mockReturnValue({
24
+ statusCode: 201,
25
+ }),
26
+ ),
27
+ badRequest: jest.fn().mockReturnValue(
28
+ jest.fn().mockReturnValue({
29
+ statusCode: 400,
30
+ }),
31
+ ),
32
+ internalServerError: jest.fn().mockReturnValue(
33
+ jest.fn().mockReturnValue({
34
+ statusCode: 500,
35
+ }),
36
+ ),
37
+ },
38
+ emitEventType: jest.fn().mockReturnValue(
39
+ jest.fn().mockReturnValue({
40
+ statusCode: 201,
41
+ }),
42
+ ),
43
+ },
44
+ } as unknown as ConnectorSDKInterface;
45
+ let httpClientMock: HttpClientSDKInterface;
46
+
47
+ beforeEach(() => {
48
+ httpClientMock = {
49
+ request: jest.fn().mockResolvedValue({
50
+ success: true,
51
+ statusCode: 201,
52
+ }),
53
+ } as unknown as HttpClientSDKInterface;
54
+
55
+ handler = new ActionsHandler(sdkMock, httpClientMock);
56
+ });
57
+
58
+ afterEach(() => {
59
+ jest.clearAllMocks();
60
+ });
61
+
62
+ it('should be defined', () => {
63
+ expect(handler).toBeDefined();
64
+ });
65
+
66
+ describe('JOB actions', () => {
67
+ const action = {
68
+ config: {
69
+ parsedTemplates: {
70
+ action: jest.fn().mockReturnValue('prmv.create.prdh_prdr'),
71
+ params: jest.fn().mockReturnValue(
72
+ JSON.stringify({
73
+ prmv_oms_1: 'API materiaal test',
74
+ cred_num: 'some-cred_num',
75
+ prdh_num: '56',
76
+ prdr_num: '78',
77
+ }),
78
+ ),
79
+ },
80
+ },
81
+ } as unknown as ActionInterface;
82
+
83
+ const jobMessage = {
84
+ type: 'JOB',
85
+ actionIdentifier: 'action-1',
86
+ actionVersion: 'latest',
87
+ payload: { some: 'data' },
88
+ } as unknown as XodJobType;
89
+
90
+ it('should execute request on action', async () => {
91
+ const response = await handler.callbackFunctionChain(jobMessage, action);
92
+
93
+ expect(response.statusCode).toBe(201);
94
+ expect(httpClientMock.request).toHaveBeenCalledWith(
95
+ 'POST',
96
+ '/web/v3/MKG/Documents/prdr/1+56+78/prdr_prmv',
97
+ {
98
+ data: {
99
+ request: {
100
+ InputData: {
101
+ prmv: [
102
+ {
103
+ prmv_oms_1: 'API materiaal test',
104
+ cred_num: 'some-cred_num',
105
+ },
106
+ ],
107
+ },
108
+ },
109
+ },
110
+ },
111
+ );
112
+ });
113
+
114
+ it('should handle test run without firing request', async () => {
115
+ const testRunMessage = {
116
+ ...jobMessage,
117
+ testRun: true,
118
+ } as XodJobType;
119
+
120
+ const response = await handler.callbackFunctionChain(
121
+ testRunMessage,
122
+ action,
123
+ );
124
+
125
+ expect(response.statusCode).toBe(201);
126
+ expect(httpClientMock.request).not.toHaveBeenCalled();
127
+ });
128
+
129
+ it('should reject on unknown action', async () => {
130
+ const action = {
131
+ config: {
132
+ parsedTemplates: {
133
+ action: jest.fn().mockReturnValue('prmv.blaat'),
134
+ params: jest.fn().mockReturnValue(JSON.stringify({})),
135
+ },
136
+ },
137
+ } as unknown as ActionInterface;
138
+
139
+ const response = await handler.callbackFunctionChain(jobMessage, action);
140
+
141
+ expect(response.statusCode).toBe(400);
142
+ expect(httpClientMock.request).not.toHaveBeenCalled();
143
+ });
144
+
145
+ it('should reject on unknown table', async () => {
146
+ const action = {
147
+ config: {
148
+ parsedTemplates: {
149
+ action: jest.fn().mockReturnValue('blaat.blaat'),
150
+ params: jest.fn().mockReturnValue(JSON.stringify({})),
151
+ },
152
+ },
153
+ } as unknown as ActionInterface;
154
+
155
+ const response = await handler.callbackFunctionChain(jobMessage, action);
156
+
157
+ expect(response.statusCode).toBe(400);
158
+ expect(httpClientMock.request).not.toHaveBeenCalled();
159
+ });
160
+
161
+ it('should not fail on unsupported action format', async () => {
162
+ const action = {
163
+ config: {
164
+ parsedTemplates: {
165
+ action: jest.fn().mockReturnValue('blaat'),
166
+ params: jest.fn().mockReturnValue(JSON.stringify({})),
167
+ },
168
+ },
169
+ } as unknown as ActionInterface;
170
+
171
+ const response = await handler.callbackFunctionChain(jobMessage, action);
172
+
173
+ expect(response.statusCode).toBe(400);
174
+ expect(httpClientMock.request).not.toHaveBeenCalled();
175
+ });
176
+ });
177
+ });
@@ -0,0 +1,143 @@
1
+ import {
2
+ CompileDelegate,
3
+ ConnectorSDKInterface,
4
+ HttpClientSDKInterface,
5
+ HttpError,
6
+ } from '@transai/connector-runtime-sdk';
7
+ import {
8
+ ActionInterface,
9
+ KafkaCallbackResponse,
10
+ XodJobType,
11
+ } from '@xip-online-data/types';
12
+
13
+ import { MKG_TABLES } from './tables/_all';
14
+
15
+ export class ActionsHandler {
16
+ readonly #connectorSDK: ConnectorSDKInterface;
17
+
18
+ readonly #httpClient: HttpClientSDKInterface;
19
+
20
+ constructor(
21
+ connectorSDK: ConnectorSDKInterface,
22
+ httpClient: HttpClientSDKInterface,
23
+ ) {
24
+ this.#connectorSDK = connectorSDK;
25
+ this.#httpClient = httpClient;
26
+ }
27
+
28
+ get callbackFunctionChain(): (
29
+ message: XodJobType,
30
+ action: ActionInterface,
31
+ ) => Promise<KafkaCallbackResponse> {
32
+ return this.#jobCallbackFunction(
33
+ this.#connectorSDK.receiver.emitEventType(
34
+ this.#connectorSDK.receiver.responses.created(),
35
+ ),
36
+ );
37
+ }
38
+
39
+ #jobCallbackFunction(
40
+ callbackFunction: (message: XodJobType) => Promise<KafkaCallbackResponse>,
41
+ ) {
42
+ return async (
43
+ message: XodJobType,
44
+ action: ActionInterface,
45
+ ): Promise<KafkaCallbackResponse> => {
46
+ try {
47
+ this.#connectorSDK.logger.debug(
48
+ `[MKG] Apply templates on payload: ${JSON.stringify(message.payload)}, action: ${JSON.stringify(action)}`,
49
+ );
50
+
51
+ const handleBars = action.config['parsedTemplates'] as {
52
+ action: CompileDelegate;
53
+ params: CompileDelegate;
54
+ };
55
+
56
+ const parsedAction = handleBars
57
+ .action({
58
+ inputs: message.payload,
59
+ })
60
+ .trim();
61
+
62
+ const parsedParams = handleBars
63
+ .params({
64
+ inputs: message.payload,
65
+ })
66
+ .trim();
67
+
68
+ const parsedParamsJson = JSON.parse(parsedParams);
69
+ this.#connectorSDK.logger.debug(
70
+ `[MKG] Parsed action: ${parsedAction}, params json: ${JSON.stringify(parsedParamsJson)}`,
71
+ );
72
+
73
+ const [tableIdentifier, ...rest] = parsedAction.split('.');
74
+ const actionIdentifier = rest.join('.');
75
+ // eslint-disable-next-line security/detect-object-injection
76
+ const mkgTable = MKG_TABLES[tableIdentifier];
77
+ if (!mkgTable) {
78
+ this.#connectorSDK.logger.warn(
79
+ `[MKG] Unknown table identifier "${tableIdentifier}" in message ${message.eventId}`,
80
+ );
81
+ return this.#connectorSDK.receiver.responses.badRequest(
82
+ `[MKG] Unknown table identifier "${tableIdentifier}"`,
83
+ )(message);
84
+ }
85
+
86
+ const mkgAction = mkgTable.action(actionIdentifier);
87
+ if (!mkgAction) {
88
+ const logMessage = `[MKG] Unknown action identifier "${actionIdentifier}" within table "${tableIdentifier}" in message ${message.eventId}`;
89
+ this.#connectorSDK.logger.warn(logMessage);
90
+ return this.#connectorSDK.receiver.responses.badRequest(logMessage)(
91
+ message,
92
+ );
93
+ }
94
+
95
+ if (message.testRun) {
96
+ this.#connectorSDK.logger.info(
97
+ `[MKG] Test run for ${message.eventId} with payload ${parsedParams} to action ${parsedAction}`,
98
+ );
99
+ return callbackFunction(message);
100
+ }
101
+
102
+ try {
103
+ const response = await this.#httpClient.request(
104
+ mkgAction.method,
105
+ `/web/v3/MKG/Documents/${mkgAction.path(parsedParamsJson)}`,
106
+ {
107
+ ...(['POST', 'PUT'].includes(mkgAction.method)
108
+ ? {
109
+ data: {
110
+ request: {
111
+ InputData: {
112
+ [tableIdentifier]: [
113
+ mkgAction.fieldSet(parsedParamsJson),
114
+ ],
115
+ },
116
+ },
117
+ },
118
+ }
119
+ : {}),
120
+ },
121
+ );
122
+
123
+ this.#connectorSDK.logger.debug(
124
+ `[MKG] Got action "${actionIdentifier}" of table "${tableIdentifier}" response from MKG: ${JSON.stringify(response)}`,
125
+ );
126
+ } catch (error: HttpError | unknown) {
127
+ const logMessage = `[MKG] Failed to perform action "${actionIdentifier}" of table "${tableIdentifier}": (${(error as HttpError)?.status}) ${(error as HttpError)?.error ?? (error as Error)?.message}`;
128
+ this.#connectorSDK.logger.error(logMessage);
129
+
130
+ return this.#connectorSDK.receiver.responses.unprocessableEntity(
131
+ logMessage,
132
+ )(message);
133
+ }
134
+
135
+ return callbackFunction(message);
136
+ } catch (error: unknown) {
137
+ return this.#connectorSDK.receiver.responses.internalServerError(
138
+ error instanceof Error ? error.message : 'Unknown error occurred',
139
+ )(message);
140
+ }
141
+ };
142
+ }
143
+ }
@@ -0,0 +1,219 @@
1
+ import {
2
+ ConnectorSDKInterface,
3
+ HttpClientSDKInterface,
4
+ HttpRequestOptionsFormatter,
5
+ } from '@transai/connector-runtime-sdk';
6
+ import { ConnectorInterface } from '@xip-online-data/types';
7
+
8
+ import { ActionsHandler } from './actions-handler';
9
+ import { ConnectorRunnerMkg } from './connector-runner-mkg';
10
+ import { ExtractorService } from './extractor.service';
11
+ import { ConnectorConfig, SESSION_EXPIRATION_SECONDS } from './types';
12
+
13
+ jest.mock('./actions-handler');
14
+ jest.mock('./extractor.service');
15
+
16
+ describe('ConnectorRunnerMkg', () => {
17
+ let runner: ConnectorRunnerMkg;
18
+
19
+ const testEndpoint = '/test-endpoint';
20
+ const sessionId = 'abcdef1234567890';
21
+
22
+ let requestFormatter: HttpRequestOptionsFormatter;
23
+
24
+ const httpClientMock = {
25
+ setRequestOptionsFormatter: jest.fn().mockImplementation((formatter) => {
26
+ requestFormatter = formatter;
27
+ return httpClientMock;
28
+ }),
29
+ post: jest.fn().mockImplementation(() => ({
30
+ status: 200,
31
+ headers: {
32
+ 'set-cookie': [`JSESSIONID=${sessionId}; Path=/; HttpOnly`],
33
+ },
34
+ })),
35
+ } as unknown as HttpClientSDKInterface;
36
+ const connector = {} as ConnectorInterface;
37
+ const sdkMock = {
38
+ logger: {
39
+ warn: jest.fn(),
40
+ verbose: jest.fn(),
41
+ error: jest.fn(),
42
+ },
43
+ config: {
44
+ server: 'saas-test.mkg.eu',
45
+ username: 'some-username',
46
+ password: 'some-password',
47
+ apiToken: 'some-api-token',
48
+ tables: {
49
+ arti: true,
50
+ unknown_table: true,
51
+ clch: {
52
+ fields: [
53
+ 'clch_volgnummer',
54
+ 'clch_op_dek_uit',
55
+ 'clch_vofr_oms_1',
56
+ 'unknown',
57
+ ],
58
+ },
59
+ },
60
+ },
61
+ processing: {
62
+ registerInterval: jest.fn(),
63
+ },
64
+ receiver: {
65
+ emitEventType: jest.fn(),
66
+ responses: {
67
+ created: jest.fn(),
68
+ },
69
+ registerCallback: jest.fn(),
70
+ },
71
+ httpClient: jest.fn().mockReturnValue(httpClientMock),
72
+ } as unknown as ConnectorSDKInterface<ConnectorConfig>;
73
+ const actionsHandlerCallbackChain = jest.fn();
74
+
75
+ beforeEach(() => {
76
+ (ActionsHandler as jest.Mock).mockImplementation((sdk, httpClient) => {
77
+ expect(sdk).toBe(sdkMock);
78
+ expect(httpClient).toBe(httpClientMock);
79
+
80
+ return {
81
+ callbackFunctionChain: actionsHandlerCallbackChain,
82
+ } as unknown as ActionsHandler;
83
+ });
84
+
85
+ runner = new ConnectorRunnerMkg(connector, sdkMock);
86
+ });
87
+
88
+ afterEach(() => {
89
+ jest.clearAllMocks();
90
+ });
91
+
92
+ it('should be defined', () => {
93
+ expect(runner).toBeDefined();
94
+ });
95
+
96
+ describe('initialization', () => {
97
+ beforeEach(() => {
98
+ (ExtractorService as jest.Mock).mockImplementation(
99
+ (sdk, httpClient, tableId, table) => {
100
+ expect(sdk).toBe(sdkMock);
101
+ expect(httpClient).toBe(httpClientMock);
102
+
103
+ switch (tableId) {
104
+ case 'arti':
105
+ expect(table).toEqual(
106
+ expect.objectContaining({
107
+ identifier: 'arti',
108
+ }),
109
+ );
110
+ break;
111
+ case 'clch':
112
+ expect(table).toEqual(
113
+ expect.objectContaining({
114
+ identifier: 'clch',
115
+ fields: [
116
+ 'clch_volgnummer',
117
+ 'clch_op_dek_uit',
118
+ 'clch_vofr_oms_1',
119
+ ],
120
+ }),
121
+ );
122
+ break;
123
+ default:
124
+ expect(false).toBe(true);
125
+ break;
126
+ }
127
+
128
+ return {} as ExtractorService;
129
+ },
130
+ );
131
+ });
132
+
133
+ it('should initialize without errors', async () => {
134
+ await expect(runner.init()).resolves.not.toThrow();
135
+
136
+ expect(sdkMock.httpClient).toHaveBeenCalledWith({
137
+ baseUrl: 'https://saas-test.mkg.eu:443/mkg',
138
+ });
139
+ expect(sdkMock.processing.registerInterval).toHaveBeenCalledTimes(2);
140
+ expect(sdkMock.processing.registerInterval).toHaveBeenNthCalledWith(
141
+ 1,
142
+ 300,
143
+ expect.any(Object),
144
+ { immediate: true },
145
+ );
146
+
147
+ expect(sdkMock.processing.registerInterval).toHaveBeenNthCalledWith(
148
+ 2,
149
+ 60,
150
+ expect.any(Object),
151
+ { immediate: true },
152
+ );
153
+ });
154
+ });
155
+
156
+ describe('token fetching', () => {
157
+ it('should have defined the formatter', () => {
158
+ expect(requestFormatter).toBeDefined();
159
+ });
160
+
161
+ it('should format requests with authentication', async () => {
162
+ const formattedOptions = await requestFormatter(
163
+ {
164
+ data: 'some-data',
165
+ },
166
+ 'GET',
167
+ testEndpoint,
168
+ );
169
+
170
+ expect(formattedOptions).toEqual(
171
+ expect.objectContaining({
172
+ headers: expect.objectContaining({
173
+ Cookie: `JSESSIONID=${sessionId}`,
174
+ 'X-CustomerID': 'some-api-token',
175
+ }),
176
+ }),
177
+ );
178
+ });
179
+
180
+ it('should format requests with custom header', async () => {
181
+ const formattedOptions = await requestFormatter(
182
+ {
183
+ headers: {
184
+ 'Custom-Header': 'custom-value',
185
+ },
186
+ },
187
+ 'GET',
188
+ testEndpoint,
189
+ );
190
+
191
+ expect(formattedOptions).toEqual(
192
+ expect.objectContaining({
193
+ headers: expect.objectContaining({
194
+ 'Custom-Header': 'custom-value',
195
+ Cookie: `JSESSIONID=${sessionId}`,
196
+ 'X-CustomerID': 'some-api-token',
197
+ }),
198
+ }),
199
+ );
200
+ });
201
+
202
+ it('should request token only once', async () => {
203
+ await requestFormatter({}, 'GET', testEndpoint);
204
+ await requestFormatter({}, 'GET', testEndpoint);
205
+
206
+ expect(httpClientMock.post).toHaveBeenCalledTimes(1);
207
+ });
208
+
209
+ it('should renew token after expiration', async () => {
210
+ jest.useFakeTimers();
211
+
212
+ await requestFormatter({}, 'GET', testEndpoint);
213
+ jest.setSystemTime(new Date().getTime() + SESSION_EXPIRATION_SECONDS);
214
+ await requestFormatter({}, 'GET', testEndpoint);
215
+
216
+ expect(httpClientMock.post).toHaveBeenCalledTimes(2);
217
+ });
218
+ });
219
+ });