ti2 1.0.122 → 1.0.123
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 +28 -1
- package/__tests__/plugin-capabilities.js +42 -0
- package/api.yml +76 -13
- package/controllers/README.md +23 -34
- package/controllers/__tests__/bookings-searchProducts.js +386 -227
- package/controllers/__tests__/product-search-ttr.js +39 -0
- package/controllers/__tests__/user.js +5 -1
- package/controllers/app.js +1 -1
- package/controllers/bookings.js +278 -152
- package/controllers/user.js +1 -0
- package/index.js +11 -1
- package/package.json +1 -1
- package/worker/__tests__/job-api.js +55 -3
- package/worker/__tests__/job-plugin.js +3 -3
- package/worker/index.js +16 -1
package/index.js
CHANGED
|
@@ -60,6 +60,7 @@ const axiosSafeResponse = response => {
|
|
|
60
60
|
|
|
61
61
|
module.exports = async ({
|
|
62
62
|
apiDocs = true,
|
|
63
|
+
pluginCapabilities: pluginCapabilitiesParam = {},
|
|
63
64
|
plugins: pluginsParam = {},
|
|
64
65
|
pluginsInstantiated = false,
|
|
65
66
|
port: portParam,
|
|
@@ -83,7 +84,7 @@ module.exports = async ({
|
|
|
83
84
|
// Create a new axios instance for this plugin and configure SSL if needed
|
|
84
85
|
const pluginAxios = axios.create();
|
|
85
86
|
configureAxiosSSL(pluginAxios, process.env.SSL_INSECURE_ALLOWED_DOMAINS);
|
|
86
|
-
|
|
87
|
+
|
|
87
88
|
const pluginInstance = await new Plugin({
|
|
88
89
|
cache: {
|
|
89
90
|
drop: args => cache.drop({ ...args, pluginName }),
|
|
@@ -99,9 +100,18 @@ module.exports = async ({
|
|
|
99
100
|
events: ti2Events,
|
|
100
101
|
name: pluginName,
|
|
101
102
|
...params,
|
|
103
|
+
capabilities: R.pathOr({}, [pluginName], pluginCapabilitiesParam),
|
|
102
104
|
});
|
|
103
105
|
return pluginInstance;
|
|
104
106
|
});
|
|
107
|
+
plugins.forEach(plugin => {
|
|
108
|
+
const configuredCapabilities = R.path([plugin.name], pluginCapabilitiesParam);
|
|
109
|
+
Object.assign(plugin, {
|
|
110
|
+
capabilities: configuredCapabilities === undefined
|
|
111
|
+
? plugin.capabilities || {}
|
|
112
|
+
: configuredCapabilities,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
105
115
|
if (worker) {
|
|
106
116
|
return require('./worker/index')({ plugins });
|
|
107
117
|
}
|
package/package.json
CHANGED
|
@@ -52,12 +52,17 @@ jest.mock('../queue', () => {
|
|
|
52
52
|
})
|
|
53
53
|
},
|
|
54
54
|
addJob: jest.fn().mockResolvedValue('test-job-id'),
|
|
55
|
-
allDone: jest.fn().mockResolvedValue(true)
|
|
55
|
+
allDone: jest.fn().mockResolvedValue(true),
|
|
56
|
+
saveResult: jest.fn().mockResolvedValue(undefined),
|
|
56
57
|
};
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
// Import the mocked queue and the actual worker module
|
|
60
|
-
const {
|
|
61
|
+
const {
|
|
62
|
+
addJob,
|
|
63
|
+
queue,
|
|
64
|
+
saveResult,
|
|
65
|
+
} = require('../queue');
|
|
61
66
|
const actualWorkerModule = require('../index'); // Actual worker module
|
|
62
67
|
|
|
63
68
|
describe('worker: API job handling', () => {
|
|
@@ -86,7 +91,10 @@ describe('worker: API job handling', () => {
|
|
|
86
91
|
axios.mockImplementation(config => {
|
|
87
92
|
// This mock is for the internal axios call made by the worker
|
|
88
93
|
if (config.url && config.url.includes('/products/testAppKey/testUserId/testTokenHint/search')) {
|
|
89
|
-
return Promise.resolve({
|
|
94
|
+
return Promise.resolve({
|
|
95
|
+
status: 200,
|
|
96
|
+
data: { success: true, products: [{ productId: 'incidental-product' }] },
|
|
97
|
+
});
|
|
90
98
|
}
|
|
91
99
|
console.error('Unexpected axios call to mock:', config);
|
|
92
100
|
return Promise.reject(new Error(`Unexpected axios call in mock for URL: ${config.url}`));
|
|
@@ -117,6 +125,10 @@ describe('worker: API job handling', () => {
|
|
|
117
125
|
actualJobHandler = null; // Clear captured handler
|
|
118
126
|
});
|
|
119
127
|
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
saveResult.mockClear();
|
|
130
|
+
});
|
|
131
|
+
|
|
120
132
|
it('should create a job of type "api" for the product search endpoint and send it to the API server', async () => {
|
|
121
133
|
|
|
122
134
|
// 1. Test that we can create a job
|
|
@@ -169,5 +181,45 @@ describe('worker: API job handling', () => {
|
|
|
169
181
|
}),
|
|
170
182
|
})
|
|
171
183
|
);
|
|
184
|
+
expect(saveResult).toHaveBeenCalledWith({
|
|
185
|
+
id: jobId,
|
|
186
|
+
resultValue: expect.objectContaining({
|
|
187
|
+
ti2CatalogProductCount: null,
|
|
188
|
+
ti2ReturnedProductCount: null,
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('reports committed and returned counts for a full catalog refresh', async () => {
|
|
194
|
+
axios.mockResolvedValueOnce({
|
|
195
|
+
status: 200,
|
|
196
|
+
data: {
|
|
197
|
+
products: [],
|
|
198
|
+
catalogRefreshOutcome: 'empty_result_preserved_cache',
|
|
199
|
+
cacheUpdated: false,
|
|
200
|
+
cachePreserved: true,
|
|
201
|
+
cachedProductCount: 7,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
const job = {
|
|
205
|
+
id: 'catalog-refresh-job',
|
|
206
|
+
data: {
|
|
207
|
+
type: 'api',
|
|
208
|
+
method: 'POST',
|
|
209
|
+
url: '/products/testAppKey/testUserId/testTokenHint/search',
|
|
210
|
+
headers: { 'content-type': 'application/json' },
|
|
211
|
+
payload: { forceRefresh: true, backgroundJob: true },
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await actualJobHandler(job);
|
|
216
|
+
|
|
217
|
+
expect(saveResult).toHaveBeenCalledWith({
|
|
218
|
+
id: job.id,
|
|
219
|
+
resultValue: expect.objectContaining({
|
|
220
|
+
ti2CatalogProductCount: 7,
|
|
221
|
+
ti2ReturnedProductCount: 0,
|
|
222
|
+
}),
|
|
223
|
+
});
|
|
172
224
|
});
|
|
173
225
|
});
|
|
@@ -27,7 +27,7 @@ const mockBookingsPostProcessAction = jest.fn();
|
|
|
27
27
|
jest.mock('../../controllers/bookings', () => {
|
|
28
28
|
return jest.fn(() => ({
|
|
29
29
|
// This action name must match what's in job.data.postProcess.action
|
|
30
|
-
$
|
|
30
|
+
$postProcessAction: mockBookingsPostProcessAction,
|
|
31
31
|
// Add other methods if the worker's initialization of bookingsCtrl calls them
|
|
32
32
|
}));
|
|
33
33
|
});
|
|
@@ -111,7 +111,7 @@ describe('worker: Plugin job handling', () => {
|
|
|
111
111
|
inTesting: true,
|
|
112
112
|
postProcess: {
|
|
113
113
|
controller: 'bookings',
|
|
114
|
-
action: '$
|
|
114
|
+
action: '$postProcessAction',
|
|
115
115
|
args: { staticArg1: 'value1', appKey: mockPluginName }, // Static args for postProcess
|
|
116
116
|
},
|
|
117
117
|
};
|
|
@@ -192,7 +192,7 @@ describe('worker: Plugin job handling', () => {
|
|
|
192
192
|
inTesting: true,
|
|
193
193
|
postProcess: {
|
|
194
194
|
controller: 'bookings',
|
|
195
|
-
action: '$
|
|
195
|
+
action: '$postProcessAction',
|
|
196
196
|
args: { appKey: mockPluginName },
|
|
197
197
|
},
|
|
198
198
|
};
|
package/worker/index.js
CHANGED
|
@@ -100,7 +100,22 @@ const worker = ({ plugins: pluginsParam }) => { // pluginsParam are instantiated
|
|
|
100
100
|
jobLog(`Worker internal server for job ${jobId} closed.`);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
const isCatalogRefreshResult = Boolean(
|
|
104
|
+
result && typeof result.catalogRefreshOutcome === 'string',
|
|
105
|
+
);
|
|
106
|
+
const returnedProducts = isCatalogRefreshResult && result.products;
|
|
107
|
+
resultValue = {
|
|
108
|
+
code,
|
|
109
|
+
result,
|
|
110
|
+
success: code >= 200 && code < 300,
|
|
111
|
+
ti2CatalogProductCount: isCatalogRefreshResult
|
|
112
|
+
&& Number.isInteger(result.cachedProductCount)
|
|
113
|
+
? result.cachedProductCount
|
|
114
|
+
: null,
|
|
115
|
+
ti2ReturnedProductCount: Array.isArray(returnedProducts)
|
|
116
|
+
? returnedProducts.length
|
|
117
|
+
: null,
|
|
118
|
+
};
|
|
104
119
|
|
|
105
120
|
} else if (type === 'callback') {
|
|
106
121
|
const { callbackUrl, request, result } = jobDataPayload; // jobDataPayload is job.data.payload
|