Skip to content
This repository was archived by the owner on May 28, 2023. It is now read-only.

Commit 3c37a3c

Browse files
author
Christian Ewald
authored
Merge branch 'develop' into feature/refactor-storefront-query-builder
2 parents f049f13 + 42df865 commit 3c37a3c

File tree

5 files changed

+119
-76
lines changed

5 files changed

+119
-76
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Varnish Cache with autoinvalidation by Cache tags as addon - @Fifciu
1818
- Add `resetPasswordUsingResetToken` to `magento1` platform - @cewald (#415)
1919
- Update to `storefront-query-builder` version `1.0.0` - @cewald (#429)
20+
- Add `composeError` in './magento2/o2m.js' - @flancer64 (#422)
21+
- Explicit data extraction from 'Error' objects - @flancer64 (#424)
2022

2123
### Fixed
2224
- add es7 support for map url module and fixed default index for es config - @gibkigonzo
2325
- Add correct paths for production build - @cewald (#407)
2426
- Fix MSI default stock id value
27+
- Add outputFormatter to response from cache - @gibkigonzo (#428)
28+
29+
30+
## [1.11.1] - 2020.03.17
31+
32+
### Added
33+
- Add save address on place order - @lucasqm (#394)
34+
- Add ElasticSearch client support for HTTP authentication - @cewald (#397)
35+
- Add error handling for catalog and add header 'X-VS-Cache-Tags' to response - @gibkigonzo
36+
37+
### Fixed
38+
- Add fallback for `sourcePriceInclTax` and `finalPriceInclTax` in `magento1` platform - @cewald (#398)
39+
- Add default ES index to config and update `getStockList` - @gibkigonzo (#405)
40+
- Makes elastic-stock extension compatible with both ES5 and ES7. Allows for stock fetch of configurable children that is set as "Not Visible Individually" - @didkan (#410)
41+
2542

2643
## [1.11.0] - 2019.12.20
2744

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-storefront-api",
3-
"version": "1.11.0",
3+
"version": "1.11.1",
44
"private": true,
55
"description": "vue-storefront API and data services",
66
"main": "dist",

src/api/catalog.ts

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import cache from '../lib/cache-instance'
66
import { sha3_224 } from 'js-sha3'
77
import AttributeService from './attribute/service'
88
import bodybuilder from 'bodybuilder'
9-
import { elasticsearch, SearchQuery } from 'storefront-query-builder'
109
import loadCustomFilters from '../helpers/loadCustomFilters'
10+
import { elasticsearch, SearchQuery } from 'storefront-query-builder'
11+
import { apiError } from '../lib/util'
1112

1213
function _cacheStorageHandler (config, result, hash, tags) {
1314
if (config.server.useOutputCache && cache) {
@@ -111,64 +112,55 @@ export default ({config, db}) => async function (req, res, body) {
111112
body: requestBody,
112113
json: true,
113114
auth: auth
114-
}, (_err, _res, _resBody) => { // TODO: add caching layer to speed up SSR? How to invalidate products (checksum on the response BEFORE processing it)
115-
if (_resBody && _resBody.hits && _resBody.hits.hits) { // we're signing up all objects returned to the client to be able to validate them when (for example order)
116-
const factory = new ProcessorFactory(config)
117-
const tagsArray = []
118-
if (config.server.useOutputCache && cache) {
119-
const tagPrefix = entityType[0].toUpperCase() // first letter of entity name: P, T, A ...
120-
tagsArray.push(entityType)
121-
_resBody.hits.hits.map(item => {
122-
if (item._source.id) { // has common identifier
123-
tagsArray.push(`${tagPrefix}${item._source.id}`)
124-
}
125-
})
126-
}
127-
128-
let resultProcessor = factory.getAdapter(entityType, indexName, req, res)
129-
130-
if (!resultProcessor) { resultProcessor = factory.getAdapter('default', indexName, req, res) } // get the default processor
131-
if (entityType === 'product') {
132-
resultProcessor.process(_resBody.hits.hits, groupId).then(async (result) => {
133-
_resBody.hits.hits = result
134-
if (config.get('varnish.enabled')) {
135-
// Add tags to cache, so we can display them in response headers then
136-
_cacheStorageHandler(config, {
137-
..._resBody,
138-
tags: tagsArray
139-
}, reqHash, tagsArray)
140-
} else {
141-
_cacheStorageHandler(config, _resBody, reqHash, tagsArray)
142-
}
143-
if (_resBody.aggregations && config.entities.attribute.loadByAttributeMetadata) {
144-
const attributeListParam = AttributeService.transformAggsToAttributeListParam(_resBody.aggregations)
145-
// find attribute list
146-
const attributeList = await AttributeService.list(attributeListParam, config, indexName)
147-
_resBody.attribute_metadata = attributeList.map(AttributeService.transformToMetadata)
148-
}
149-
res.json(_outputFormatter(_resBody, responseFormat));
150-
}).catch((err) => {
151-
console.error(err)
152-
})
153-
} else {
154-
resultProcessor.process(_resBody.hits.hits).then((result) => {
155-
_resBody.hits.hits = result
156-
if (config.get('varnish.enabled')) {
157-
// Add tags to cache, so we can display them in response headers then
158-
_cacheStorageHandler(config, {
159-
..._resBody,
160-
tags: tagsArray
161-
}, reqHash, tagsArray)
162-
} else {
163-
_cacheStorageHandler(config, _resBody, reqHash, tagsArray)
164-
}
165-
res.json(_outputFormatter(_resBody, responseFormat));
166-
}).catch((err) => {
167-
console.error(err)
168-
})
115+
}, async (_err, _res, _resBody) => { // TODO: add caching layer to speed up SSR? How to invalidate products (checksum on the response BEFORE processing it)
116+
if (_err || _resBody.error) {
117+
apiError(res, _err || _resBody.error);
118+
return
119+
}
120+
try {
121+
if (_resBody && _resBody.hits && _resBody.hits.hits) { // we're signing up all objects returned to the client to be able to validate them when (for example order)
122+
const factory = new ProcessorFactory(config)
123+
const tagsArray = []
124+
if (config.server.useOutputCache && cache) {
125+
const tagPrefix = entityType[0].toUpperCase() // first letter of entity name: P, T, A ...
126+
tagsArray.push(entityType)
127+
_resBody.hits.hits.map(item => {
128+
if (item._source.id) { // has common identifier
129+
tagsArray.push(`${tagPrefix}${item._source.id}`)
130+
}
131+
})
132+
const cacheTags = tagsArray.join(' ')
133+
res.setHeader('X-VS-Cache-Tags', cacheTags)
134+
}
135+
136+
let resultProcessor = factory.getAdapter(entityType, indexName, req, res)
137+
138+
if (!resultProcessor) { resultProcessor = factory.getAdapter('default', indexName, req, res) } // get the default processor
139+
140+
const productGroupId = entityType === 'product' ? groupId : undefined
141+
const result = await resultProcessor.process(_resBody.hits.hits, productGroupId)
142+
_resBody.hits.hits = result
143+
if (entityType === 'product' && _resBody.aggregations && config.entities.attribute.loadByAttributeMetadata) {
144+
const attributeListParam = AttributeService.transformAggsToAttributeListParam(_resBody.aggregations)
145+
// find attribute list
146+
const attributeList = await AttributeService.list(attributeListParam, config, indexName)
147+
_resBody.attribute_metadata = attributeList.map(AttributeService.transformToMetadata)
148+
}
149+
if (config.get('varnish.enabled')) {
150+
// Add tags to cache, so we can display them in response headers then
151+
_cacheStorageHandler(config, {
152+
..._resBody,
153+
tags: tagsArray
154+
}, reqHash, tagsArray)
155+
} else {
156+
_cacheStorageHandler(config, _resBody, reqHash, tagsArray)
157+
}
158+
res.json(_outputFormatter(_resBody, responseFormat));
159+
} else { // no cache storage if no results from Elastic
160+
res.json(_resBody);
169161
}
170-
} else { // no cache storage if no results from Elastic
171-
res.json(_resBody);
162+
} catch (err) {
163+
apiError(res, err);
172164
}
173165
});
174166
}
@@ -184,7 +176,7 @@ export default ({config, db}) => async function (req, res, body) {
184176
res.setHeader('X-VS-Cache-Tag', tagsHeader)
185177
delete output.tags
186178
}
187-
res.json(output)
179+
res.json(_outputFormatter(output, responseFormat));
188180
console.log(`cache hit [${req.url}], cached request: ${Date.now() - s}ms`)
189181
} else {
190182
res.setHeader('X-VS-Cache', 'Miss')

src/lib/util.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,22 @@ export function apiStatus (res, result = 'OK', code = 200, meta = null) {
7878
return result;
7979
}
8080

81-
/** Creates a api error status Express Response object.
82-
* @param {express.Response} res Express HTTP Response
83-
* @param {number} [code=200] Status code to send on success
84-
* @param {json} [result='OK'] Text message or result information object
81+
/**
82+
* Creates an error for API status of Express Response object.
83+
*
84+
* @param {express.Response} res Express HTTP Response
85+
* @param {object|string} error Error object or error message
86+
* @return {json} [result='OK'] Text message or result information object
8587
*/
86-
export function apiError (res, errorObj, code = 500) {
87-
const result = errorObj.message ? errorObj.message : (errorObj.errorMessage ? errorObj.errorMessage : errorObj);
88-
const resultCode = errorObj.code ? errorObj.code : code;
89-
return apiStatus(res, result, resultCode)
88+
export function apiError (res, error) {
89+
let errorCode = error.code || error.status || 500;
90+
let errorMessage = error.errorMessage || error;
91+
if (error instanceof Error) {
92+
// Class 'Error' is not serializable with JSON.stringify, extract data explicitly.
93+
errorCode = error.code || errorCode;
94+
errorMessage = error.message;
95+
}
96+
return apiStatus(res, errorMessage, errorCode);
9097
}
9198

9299
export function encryptToken (textToken, secret) {

src/platform/magento2/o2m.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,36 @@ function processSingleOrder (orderData, config, job, done, logger = console) {
3434
const THREAD_ID = 'ORD:' + (job ? job.id : 1) + ' - '; // job id
3535
let currentStep = 1;
3636

37+
/**
38+
* Internal function to compose Error object using messages about other errors.
39+
*
40+
* 'Error' constructor should contain one message object only.
41+
* (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error)
42+
*
43+
* @param {string} message Main error message.
44+
* @param {string|array|object} errors Additional error message or error object or array of array objects.
45+
* @return {Error}
46+
*/
47+
function composeError (message, errors) {
48+
if (typeof errors === 'string') {
49+
message = message + ' ' + errors;
50+
} else if (Array.isArray(errors)) {
51+
// case with array of validation errors (ajv.ErrorObject - node_modules/ajv/lib/ajv.d.ts)
52+
errors.forEach((item) => {
53+
const part = (typeof item === 'string') ? item : (item.message || '');
54+
message = (message + ' ' + part).trim();
55+
});
56+
} else if (errors && (errors.message || errors.errorMessage)) {
57+
// I don't know possible structure of an 'errors' in this case, so I take 'apiError()' from 'src/lib/util.js'
58+
// we should use debugger to inspect this case in more details and modify code.
59+
message = message + ' ' + (errors.message || errors.errorMessage);
60+
}
61+
return new Error(message.trim());
62+
}
63+
3764
if (!validate(orderData)) { // schema validation of upcoming order
3865
logger.error(THREAD_ID + ' Order validation error!', validate.errors);
39-
done(new Error('Error while validating order object', validate.errors));
66+
done(composeError('Error while validating order object.', validate.errors));
4067

4168
if (job) job.progress(currentStep++, TOTAL_STEPS);
4269
return;
@@ -248,28 +275,28 @@ function processSingleOrder (orderData, config, job, done, logger = console) {
248275
})
249276
}).catch(err => {
250277
logger.error('Error placing an order', err, typeof err)
251-
if (job) job.attempts(6).backoff({ delay: 30 * 1000, type: 'fixed' }).save()
252-
return done(new Error('Error placing an order', err));
278+
if (job) job.attempts(6).backoff({delay: 30 * 1000, type: 'fixed'}).save()
279+
return done(composeError('Error placing an order.', err));
253280
})
254281
}).catch((errors) => {
255282
logger.error('Error while adding shipping address', errors)
256283
if (job) job.attempts(3).backoff({ delay: 60 * 1000, type: 'fixed' }).save()
257-
return done(new Error('Error while adding shipping address', errors));
284+
return done(composeError('Error while adding shipping address.', errors));
258285
})
259286
}).catch((errors) => {
260287
logger.error('Error while adding billing address', errors)
261288
if (job) job.attempts(3).backoff({ delay: 60 * 1000, type: 'fixed' }).save()
262-
return done(new Error('Error while adding billing address', errors));
289+
return done(composeError('Error while adding billing address.', errors));
263290
})
264291
}).catch((errors) => {
265292
logger.error('Error while synchronizing country list', errors)
266293
if (job) job.attempts(3).backoff({ delay: 30 * 1000, type: 'fixed' }).save()
267-
return done(new Error('Error while syncing country list', errors));
294+
return done(composeError('Error while syncing country list.', errors));
268295
})
269296
}).catch((errors) => {
270297
logger.error('Error while adding products', errors)
271298
if (job) job.attempts(3).backoff({ delay: 30 * 1000, type: 'fixed' }).save()
272-
return done(new Error('Error while adding products', errors));
299+
return done(composeError('Error while adding products.', errors));
273300
})
274301
})
275302
}
@@ -291,7 +318,7 @@ function processSingleOrder (orderData, config, job, done, logger = console) {
291318
// })
292319
}).catch(error => {
293320
logger.info(error)
294-
return done(new Error('Error while adding products', error));
321+
return done(composeError('Error while adding products.', error));
295322
}) // TODO: assign the guest cart with user at last?
296323
} else {
297324
logger.info(THREAD_ID + '< Using cartId provided with the order', cartId)

0 commit comments

Comments
 (0)