amphora 8.0.0-0 → 8.2.2

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.
@@ -57,7 +57,6 @@ jobs:
57
57
  paths:
58
58
  - node_modules
59
59
  - run: npm test
60
- - run: cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
61
60
 
62
61
  test_node14:
63
62
  docker:
@@ -12,7 +12,6 @@ const _ = require('lodash'),
12
12
 
13
13
  describe(_.startCase(filename), function () {
14
14
  let sandbox,
15
- bootstrapFake, // eslint-disable-line
16
15
  db,
17
16
  sitesFake,
18
17
  fakeLog;
@@ -41,8 +40,6 @@ describe(_.startCase(filename), function () {
41
40
  prefix: 'example4.com',
42
41
  subsite: 'example4'
43
42
  }];
44
-
45
- bootstrapFake = files.getYaml(path.resolve('./test/fixtures/config/bootstrap'));
46
43
  });
47
44
 
48
45
  beforeEach(function () {
package/lib/render.js CHANGED
@@ -87,8 +87,12 @@ function renderComponent(req, res, hrStart) {
87
87
  return components.get(_ref, locals)
88
88
  .then(cmptData => formDataForRenderer(cmptData, { _ref }, locals))
89
89
  .tap(logTime(hrStart, _ref))
90
- .then(({data, options}) => renderer.render(data, options, res))
91
- .catch(err => log('error', err, { url: _.get(res, 'locals.url') }));
90
+ .then(({ data, options }) => renderer.render(data, options, res))
91
+ .catch(err => {
92
+ log('error', err, { url: _.get(res, 'locals.url') });
93
+
94
+ return responses.handleError(res)(err);
95
+ });
92
96
  }
93
97
 
94
98
  /**
@@ -7,6 +7,7 @@ const _ = require('lodash'),
7
7
  expect = require('chai').expect,
8
8
  sinon = require('sinon'),
9
9
  schema = require('./schema'),
10
+ responses = require('./responses'),
10
11
  components = require('./services/components'),
11
12
  composer = require('./services/composer'),
12
13
  createMockReq = require('../test/fixtures/mocks/req'),
@@ -81,6 +82,7 @@ describe(_.startCase(filename), function () {
81
82
  sandbox.stub(components);
82
83
  sandbox.stub(composer);
83
84
  sandbox.stub(schema);
85
+ sandbox.stub(responses, 'handleError').returns(() => {});
84
86
  lib.setLog(mockLog);
85
87
  lib.resetUriRouteHandlers();
86
88
  lib.registerRenderers({
@@ -177,6 +179,7 @@ describe(_.startCase(filename), function () {
177
179
  return fn(req, getMockRes())
178
180
  .then(() => {
179
181
  sinon.assert.calledWith(mockLog, 'error', error);
182
+ sinon.assert.calledOnce(responses.handleError);
180
183
  lib.registerRenderers(undefined);
181
184
  });
182
185
  });
package/lib/responses.js CHANGED
@@ -9,6 +9,25 @@ const _ = require('lodash'),
9
9
  filter = require('through2-filter'),
10
10
  metaController = require('./services/metadata');
11
11
 
12
+ /**
13
+ * Returns a the name of the log level appropriate for a given HTTP response code.
14
+ * - 5xx = error
15
+ * - 4xx = warn
16
+ * - default = info
17
+ *
18
+ * @param {number} code: An HTTP status code.
19
+ * @returns {string}
20
+ */
21
+ function logLevelFromStatusCode(code) {
22
+ if (code >= 500) {
23
+ return 'error';
24
+ }
25
+ if (code >= 400) {
26
+ return 'warn';
27
+ }
28
+ return 'info';
29
+ }
30
+
12
31
  /**
13
32
  * Finds prefixToken, and removes it and anything before it.
14
33
  *
@@ -283,17 +302,17 @@ function notFound(err, res) {
283
302
  * @param {object} res
284
303
  */
285
304
  function serverError(err, res) {
286
- // error is required to be logged
287
- log('error', err.message, { stack: err.stack, url: _.get(res, 'locals.url') });
288
-
289
305
  const message = err.message || 'Server Error', // completely hide these messages from outside
290
- code = 500;
306
+ code = 500,
307
+ level = logLevelFromStatusCode(code);
308
+
309
+ log(level, err.message, { code, stack: err.stack, url: _.get(res, 'locals.url') });
291
310
 
292
311
  sendDefaultResponseForCode(code, message, res);
293
312
  }
294
313
 
295
314
  /**
296
- * All client errors should look like this.
315
+ * All unauthorized requests should look like this.
297
316
  *
298
317
  * @param {object} res
299
318
  */
@@ -312,14 +331,32 @@ function unauthorized(res) {
312
331
  * @param {object} res
313
332
  */
314
333
  function clientError(err, res) {
315
- log('error', err.message, { stack: err.stack, url: _.get(res, 'locals.url') });
316
334
  // They know it's a 400 already, we don't need to repeat the fact that its an error.
317
335
  const message = removePrefix(err.message, ':'),
318
- code = 400;
336
+ code = 400,
337
+ level = logLevelFromStatusCode(code);
338
+
339
+ log(level, err.message, { code, stack: err.stack, url: _.get(res, 'locals.url') });
319
340
 
320
341
  sendDefaultResponseForCode(code, message, res);
321
342
  }
322
343
 
344
+ /**
345
+ * This function dispatches an errors that have a manually set .status attribute.
346
+ * The .status attribute indicates that the error was intentionally constructed
347
+ * or caught and assigned a specific response code/message.
348
+ *
349
+ * @param {Error} err
350
+ * @param {object} res
351
+ */
352
+ function explicitError(err, res) {
353
+ const code = err.status,
354
+ level = logLevelFromStatusCode(code);
355
+
356
+ log(level, err.message, { code, stack: err.stack, url: _.get(res, 'locals.url') });
357
+ sendDefaultResponseForCode(code, err.message, res);
358
+ }
359
+
323
360
  /**
324
361
  * Handle errors in the standard/generic way
325
362
  * @param {object} res
@@ -330,7 +367,7 @@ function handleError(res) {
330
367
  if (err.status && err.name !== 'NotFoundError') {
331
368
  // If we're in this block, the error has a defined `status` property and
332
369
  // the error should be directed out immediately
333
- sendDefaultResponseForCode(err.status, err.message, res);
370
+ explicitError(err, res);
334
371
  } else if (err.name === 'NotFoundError' ||
335
372
  err.message.indexOf('ENOENT') !== -1 ||
336
373
  err.message.indexOf('not found') !== -1) {
@@ -111,13 +111,29 @@ describe(_.startCase(filename), function () {
111
111
  fn(res)(new Error('something'));
112
112
  });
113
113
 
114
- it('sends the error code defined in the `status` property', function (done) {
114
+ it('sends the error code defined in the `status` property, 4xx', function (done) {
115
115
  const res = createMockRes(),
116
116
  myError = new Error('something');
117
117
 
118
118
  myError.status = 403;
119
119
  expectStatus(res, 403);
120
- expectResult(res, 'sendStatus: whatever', done);
120
+ expectResult(res, 'sendStatus: whatever', function () {
121
+ sinon.assert.calledWith(fakeLog, 'warn');
122
+ done();
123
+ });
124
+ fn(res)(myError);
125
+ });
126
+
127
+ it('sends the error code defined in the `status` property, 5xx', function (done) {
128
+ const res = createMockRes(),
129
+ myError = new Error('something');
130
+
131
+ myError.status = 500;
132
+ expectStatus(res, 500);
133
+ expectResult(res, 'sendStatus: whatever', function () {
134
+ sinon.assert.calledWith(fakeLog, 'error');
135
+ done();
136
+ });
121
137
  fn(res)(myError);
122
138
  });
123
139
  });
@@ -145,7 +161,7 @@ describe(_.startCase(filename), function () {
145
161
 
146
162
  expectStatus(res, 400);
147
163
  expectResult(res, 'sendStatus: whatever', function () {
148
- sinon.assert.calledWith(fakeLog, 'error');
164
+ sinon.assert.calledWith(fakeLog, 'warn');
149
165
  done();
150
166
  });
151
167
  fn(new Error('something'), res);
@@ -31,11 +31,12 @@ function resolveComponentReferences(data, locals, filter = referenceProperty) {
31
31
  cmpt: referenceObject[referenceProperty]
32
32
  };
33
33
 
34
- if (error.status) {
35
- logObj.status = error.status;
34
+ // Errors with a .status property will be handled by lib/responses.js
35
+ // and logged with a level corresponding to the status code.
36
+ if (!error.status) {
37
+ log('error', `${error.message}`, logObj);
36
38
  }
37
39
 
38
- log('error', `${error.message}`, logObj);
39
40
 
40
41
  return bluebird.reject(error);
41
42
  });
@@ -113,7 +113,7 @@ describe(_.startCase(filename), function () {
113
113
  });
114
114
  });
115
115
 
116
- it('adds the status to the error message if it one is defined', function () {
116
+ it('adds the status to the error message if it one is defined, skips log', function () {
117
117
  const data = {
118
118
  a: {_ref: '/c/b'},
119
119
  c: {d: {_ref: '/c/e'}}
@@ -127,9 +127,8 @@ describe(_.startCase(filename), function () {
127
127
  components.get.withArgs('/c/m').returns(bluebird.reject(myError));
128
128
 
129
129
  return fn(data).catch((error) => {
130
- sinon.assert.calledOnce(logSpy);
131
130
  expect(error.message).to.equal(errorMessage);
132
- sinon.assert.calledWith(logSpy, 'error', errorMessage, { status: myError.status, cmpt: '/c/e', stack: myError.stack });
131
+ expect(error.status).to.equal(404);
133
132
  });
134
133
  });
135
134
  });
@@ -80,6 +80,30 @@ function splitCascadingData(uri, data) {
80
80
  return ops;
81
81
  }
82
82
 
83
+ /**
84
+ * Adds user info onto each op object
85
+ *
86
+ * @param {Object} locals
87
+ * @param {Object} locals.user
88
+ * @returns {Function}
89
+ */
90
+ function addUserInfoToOp(locals) {
91
+ if (!locals) {
92
+ return _.identity;
93
+ }
94
+
95
+ const { user } = locals;
96
+
97
+ function addUserProp(op) {
98
+ op.user = user;
99
+ }
100
+
101
+ return (opsList) => {
102
+ opsList.forEach(addUserProp);
103
+ return opsList;
104
+ };
105
+ }
106
+
83
107
  /**
84
108
  * Get a list of all the put operations needed to complete a cascading PUT
85
109
  *
@@ -105,8 +129,10 @@ function getPutOperations(putFn, uri, data, locals) {
105
129
  }
106
130
 
107
131
  // run each through the normal put, which may or may not hit custom component logic
108
- return bluebird.map(cmptOrLayout, ({ key, value }) => putFn(key, value, locals))
109
- .then(ops => _.filter(_.flattenDeep(ops), _.identity));
132
+ return bluebird
133
+ .map(cmptOrLayout, ({ key, value }) => putFn(key, value, locals))
134
+ .then(ops => _.filter(_.flattenDeep(ops), _.identity))
135
+ .then(addUserInfoToOp(locals));
110
136
  }
111
137
 
112
138
  /**
@@ -27,13 +27,17 @@ const _ = require('lodash'),
27
27
  * @param {string} uri
28
28
  * @param {{}} data
29
29
  * @param {[]} ops
30
+ * @param {{}} locals
30
31
  * @returns {array}
31
32
  */
32
- function addOp(uri, data, ops) {
33
+ function addOp(uri, data, ops, locals = {}) {
34
+ const { user } = locals;
35
+
33
36
  ops.push({
34
37
  type: 'put',
35
38
  key: uri,
36
- value: JSON.stringify(data)
39
+ value: JSON.stringify(data),
40
+ user
37
41
  });
38
42
 
39
43
  return ops;
@@ -257,7 +261,7 @@ function publish(uri, data, locals) {
257
261
  publishedMeta = meta;
258
262
 
259
263
  // add page PUT operation last
260
- return addOp(replaceVersion(uri, published), pageData, ops);
264
+ return addOp(replaceVersion(uri, published), pageData, ops, locals);
261
265
  });
262
266
  })
263
267
  .then(applyBatch)
@@ -306,7 +310,7 @@ function create(uri, data, locals) {
306
310
  return getPageClonePutOperations(pageData, locals)
307
311
  .then(ops => {
308
312
  pageData.layout = layoutReference;
309
- return addOp(pageReference, pageData, ops);
313
+ return addOp(pageReference, pageData, ops, locals);
310
314
  });
311
315
  })
312
316
  .then(applyBatch)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amphora",
3
- "version": "8.0.0-0",
3
+ "version": "8.2.2",
4
4
  "description": "An API mixin for Express that saves, publishes and composes data with the key-value store of your choice.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,13 +17,13 @@
17
17
  "author": "New York Media",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "amphora-auth": "^2.0.0-0",
21
- "amphora-fs": "^2.0.0-0",
20
+ "amphora-auth": "^2.0.0",
21
+ "amphora-fs": "^2.0.0",
22
22
  "basic-auth": "^1.0.4",
23
23
  "bluebird": "^3.5.1",
24
24
  "body-parser": "^1.12",
25
25
  "clay-log": "^1.2.0",
26
- "clayutils": "^3.0.0-0",
26
+ "clayutils": "^3.0.0",
27
27
  "cuid": "^1.2.5",
28
28
  "eventify": "^2.0.0",
29
29
  "express": "^4.15.3",
@@ -33,7 +33,7 @@
33
33
  "highland": "^2.11.1",
34
34
  "ioredis": "^4.0.0",
35
35
  "js-yaml": "^3.2.0",
36
- "lodash": "^4.17.20",
36
+ "lodash": "^4.14.2",
37
37
  "node-fetch": "^1.3.0",
38
38
  "passport": "^0.3.2",
39
39
  "passport-google-oauth": "^1.0.0",