apostrophe 3.53.0 → 3.55.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 (77) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/defaults.js +1 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +5 -2
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +28 -19
  5. package/modules/@apostrophecms/any-doc-type/index.js +2 -2
  6. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  7. package/modules/@apostrophecms/doc/index.js +55 -29
  8. package/modules/@apostrophecms/doc-type/index.js +11 -6
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +4 -440
  10. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +445 -0
  11. package/modules/@apostrophecms/i18n/i18n/de.json +113 -105
  12. package/modules/@apostrophecms/i18n/i18n/es.json +10 -0
  13. package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
  14. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +10 -0
  15. package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +1 -0
  17. package/modules/@apostrophecms/log/index.js +429 -0
  18. package/modules/@apostrophecms/login/index.js +47 -4
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +14 -1
  20. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -1
  21. package/modules/@apostrophecms/module/index.js +32 -6
  22. package/modules/@apostrophecms/module/lib/log.js +68 -0
  23. package/modules/@apostrophecms/page/index.js +71 -19
  24. package/modules/@apostrophecms/page/lib/legacy-migrations.js +0 -57
  25. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +8 -285
  26. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +291 -0
  27. package/modules/@apostrophecms/page-type/index.js +39 -26
  28. package/modules/@apostrophecms/piece-type/index.js +19 -11
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +2 -357
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +2 -86
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -254
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +2 -77
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -44
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +2 -64
  36. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +2 -94
  37. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +3 -47
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -82
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +2 -37
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +2 -26
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -57
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +2 -259
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +2 -38
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +2 -275
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -167
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +2 -115
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +3 -279
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +2 -83
  49. package/modules/@apostrophecms/schema/ui/apos/lib/detectChange.js +10 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +361 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +89 -0
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +257 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +81 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputBoolean.js +48 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +68 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +98 -0
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +49 -0
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +86 -0
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +41 -0
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +29 -0
  61. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +60 -0
  62. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +262 -0
  63. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +41 -0
  64. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +278 -0
  65. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +170 -0
  66. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +118 -0
  67. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +281 -0
  68. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +85 -0
  69. package/modules/@apostrophecms/template/index.js +1 -1
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -2
  71. package/modules/@apostrophecms/util/index.js +83 -13
  72. package/modules/@apostrophecms/util/lib/logger.js +19 -17
  73. package/package.json +1 -1
  74. package/test/docs.js +35 -2
  75. package/test/log.js +1765 -0
  76. package/test/pages.js +57 -0
  77. package/test-lib/util.js +1 -1
package/test/log.js ADDED
@@ -0,0 +1,1765 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert/strict');
3
+
4
+ const testModule = {
5
+ 'test-module': {
6
+ options: {
7
+ alias: 'testModule'
8
+ },
9
+ init() { }
10
+ }
11
+ };
12
+
13
+ describe('structured logging', function () {
14
+ this.timeout(t.timeout);
15
+
16
+ let apos;
17
+
18
+ after(function () {
19
+ return t.destroy(apos);
20
+ });
21
+
22
+ describe('defaults', function () {
23
+
24
+ before(async function () {
25
+ apos = await t.create({
26
+ modules: { ...testModule }
27
+ });
28
+ });
29
+
30
+ after(async function () {
31
+ await t.destroy(apos);
32
+ apos = null;
33
+ });
34
+
35
+ it('should register structured log and module log handlers', function () {
36
+ assert(apos.structuredLog);
37
+ assert(apos.testModule);
38
+ assert.equal(typeof apos.testModule.logDebug, 'function');
39
+ assert.equal(typeof apos.testModule.logInfo, 'function');
40
+ assert.equal(typeof apos.testModule.logWarn, 'function');
41
+ assert.equal(typeof apos.testModule.logError, 'function');
42
+ assert.deepEqual(apos.structuredLog.filters, {
43
+ '*': { severity: [ 'debug', 'info', 'warn', 'error' ] }
44
+ });
45
+ });
46
+
47
+ it('should format entries for readability', function () {
48
+ // id spy
49
+ const id = apos.util.generateId;
50
+ apos.util.generateId = () => 'test-id';
51
+ let savedArgs = [];
52
+
53
+ // ### DEBUG
54
+ const debug = console.debug;
55
+ console.debug = (...args) => {
56
+ savedArgs = args;
57
+ };
58
+
59
+ // Validate formatting
60
+ savedArgs = [];
61
+ apos.testModule.logDebug('event-type');
62
+ assert.equal(savedArgs[0], 'test-module: event-type\n');
63
+ assert.equal(
64
+ savedArgs[1],
65
+ `{
66
+ "module": "test-module",
67
+ "type": "event-type",
68
+ "severity": "debug"
69
+ }`
70
+ );
71
+
72
+ // Message as
73
+ savedArgs = [];
74
+ apos.structuredLog.options.messageAs = 'msg';
75
+ apos.testModule.logDebug('event-type', 'some message');
76
+ assert.equal(
77
+ savedArgs[0],
78
+ `{
79
+ "msg": "test-module: event-type: some message",
80
+ "module": "test-module",
81
+ "type": "event-type",
82
+ "severity": "debug"
83
+ }`
84
+ );
85
+ assert.equal(typeof savedArgs[1], 'undefined');
86
+ delete apos.structuredLog.options.messageAs;
87
+
88
+ // ### INFO
89
+ const info = console.info;
90
+ console.info = (...args) => {
91
+ savedArgs = args;
92
+ };
93
+
94
+ // Validate formatting
95
+ savedArgs = [];
96
+ apos.testModule.logInfo('event-type');
97
+ assert.equal(savedArgs[0], 'test-module: event-type\n');
98
+ assert.equal(
99
+ savedArgs[1],
100
+ `{
101
+ "module": "test-module",
102
+ "type": "event-type",
103
+ "severity": "info"
104
+ }`
105
+ );
106
+
107
+ // ### WARN
108
+ const warn = console.warn;
109
+ console.warn = (...args) => {
110
+ savedArgs = args;
111
+ };
112
+
113
+ // Validate formatting
114
+ savedArgs = [];
115
+ apos.testModule.logWarn('event-type');
116
+ assert.equal(savedArgs[0], 'test-module: event-type\n');
117
+ assert.equal(
118
+ savedArgs[1],
119
+ `{
120
+ "module": "test-module",
121
+ "type": "event-type",
122
+ "severity": "warn"
123
+ }`
124
+ );
125
+
126
+ // ### ERROR
127
+ const error = console.error;
128
+ console.error = (...args) => {
129
+ savedArgs = args;
130
+ };
131
+
132
+ // Validate formatting
133
+ savedArgs = [];
134
+ apos.testModule.logError('event-type');
135
+ assert.equal(savedArgs[0], 'test-module: event-type\n');
136
+ assert.equal(
137
+ savedArgs[1],
138
+ `{
139
+ "module": "test-module",
140
+ "type": "event-type",
141
+ "severity": "error"
142
+ }`
143
+ );
144
+
145
+ // With req
146
+ savedArgs = [];
147
+ apos.testModule.logError(
148
+ apos.task.getReq({
149
+ originalUrl: '/module/test',
150
+ path: '/test',
151
+ method: 'GET',
152
+ ip: '1.2.3.4',
153
+ query: { foo: 'bar' }
154
+ }),
155
+ 'event-type'
156
+ );
157
+ assert.equal(savedArgs[0], 'test-module: event-type\n');
158
+ assert.equal(
159
+ savedArgs[1],
160
+ `{
161
+ "module": "test-module",
162
+ "type": "event-type",
163
+ "severity": "error",
164
+ "url": "/module/test",
165
+ "path": "/test",
166
+ "method": "GET",
167
+ "ip": "1.2.3.4",
168
+ "query": {
169
+ "foo": "bar"
170
+ },
171
+ "requestId": "test-id"
172
+ }`
173
+ );
174
+
175
+ // With req and message as
176
+ savedArgs = [];
177
+ apos.structuredLog.options.messageAs = 'message';
178
+ apos.testModule.logError(
179
+ apos.task.getReq({
180
+ originalUrl: '/module/test',
181
+ path: '/test',
182
+ method: 'GET',
183
+ ip: '1.2.3.4',
184
+ query: { foo: 'bar' }
185
+ }),
186
+ 'event-type'
187
+ );
188
+ assert.equal(savedArgs.length, 1);
189
+ assert.equal(
190
+ savedArgs[0],
191
+ `{
192
+ "message": "test-module: event-type",
193
+ "module": "test-module",
194
+ "type": "event-type",
195
+ "severity": "error",
196
+ "url": "/module/test",
197
+ "path": "/test",
198
+ "method": "GET",
199
+ "ip": "1.2.3.4",
200
+ "query": {
201
+ "foo": "bar"
202
+ },
203
+ "requestId": "test-id"
204
+ }`
205
+ );
206
+ delete apos.structuredLog.options.messageAs;
207
+
208
+ apos.util.generateId = id;
209
+ console.debug = debug;
210
+ console.info = info;
211
+ console.warn = warn;
212
+ console.error = error;
213
+ });
214
+
215
+ it('should log formatted entry: logDebug', function () {
216
+ // id spy
217
+ const id = apos.util.generateId;
218
+ apos.util.generateId = () => 'test-id';
219
+
220
+ // debug spy
221
+ const debug = apos.util.logger.debug;
222
+ let savedArgs = [];
223
+ apos.util.logger.debug = (...args) => {
224
+ savedArgs = args;
225
+ };
226
+
227
+ // Validate
228
+ assert.throws(() => {
229
+ apos.testModule.logDebug();
230
+ }, function (err) {
231
+ assert.equal(err.message, 'Event type must be a string');
232
+ return true;
233
+ });
234
+ assert.throws(() => {
235
+ apos.testModule.logDebug(apos.task.getReq());
236
+ }, function (err) {
237
+ assert.equal(err.message, 'Event type must be a string');
238
+ return true;
239
+ });
240
+ assert.throws(() => {
241
+ apos.testModule.logDebug(null);
242
+ }, function (err) {
243
+ assert.equal(err.message, 'Event type must be a string');
244
+ return true;
245
+ });
246
+ assert.throws(() => {
247
+ apos.testModule.logDebug(1);
248
+ }, function (err) {
249
+ assert.equal(err.message, 'Event type must be a string');
250
+ return true;
251
+ });
252
+
253
+ // Format
254
+ apos.testModule.logDebug('event-type');
255
+ assert.equal(savedArgs[0], 'test-module: event-type');
256
+ assert.deepEqual(savedArgs[1], {
257
+ type: 'event-type',
258
+ severity: 'debug',
259
+ module: 'test-module'
260
+ });
261
+
262
+ apos.testModule.logDebug('event-type', 'a message');
263
+ assert.equal(savedArgs[0], 'test-module: event-type: a message');
264
+ assert.deepEqual(savedArgs[1], {
265
+ type: 'event-type',
266
+ severity: 'debug',
267
+ module: 'test-module'
268
+ });
269
+
270
+ apos.testModule.logDebug('event-type', 'a message', { foo: 'bar' });
271
+ assert.equal(savedArgs[0], 'test-module: event-type: a message');
272
+ assert.deepEqual(savedArgs[1], {
273
+ type: 'event-type',
274
+ severity: 'debug',
275
+ module: 'test-module',
276
+ foo: 'bar'
277
+ });
278
+
279
+ apos.testModule.logDebug('event-type', { foo: 'bar' });
280
+ assert.equal(savedArgs[0], 'test-module: event-type');
281
+ assert.deepEqual(savedArgs[1], {
282
+ type: 'event-type',
283
+ severity: 'debug',
284
+ module: 'test-module',
285
+ foo: 'bar'
286
+ });
287
+
288
+ apos.testModule.logDebug(apos.task.getReq({
289
+ originalUrl: '/module/test',
290
+ path: '/test',
291
+ method: 'GET',
292
+ ip: '1.2.3.4',
293
+ query: { foo: 'bar' }
294
+ }), 'event-type');
295
+ assert.equal(savedArgs[0], 'test-module: event-type');
296
+ assert.deepEqual(savedArgs[1], {
297
+ url: '/module/test',
298
+ path: '/test',
299
+ method: 'GET',
300
+ ip: '1.2.3.4',
301
+ query: { foo: 'bar' },
302
+ requestId: 'test-id',
303
+ type: 'event-type',
304
+ severity: 'debug',
305
+ module: 'test-module'
306
+ });
307
+
308
+ apos.testModule.logDebug(apos.task.getReq({
309
+ originalUrl: '/module/test',
310
+ path: '/test',
311
+ method: 'GET',
312
+ ip: '1.2.3.4',
313
+ query: { foo: 'bar' }
314
+ }), 'event-type', 'some message');
315
+ assert.equal(savedArgs[0], 'test-module: event-type: some message');
316
+ assert.deepEqual(savedArgs[1], {
317
+ url: '/module/test',
318
+ path: '/test',
319
+ method: 'GET',
320
+ ip: '1.2.3.4',
321
+ query: { foo: 'bar' },
322
+ requestId: 'test-id',
323
+ type: 'event-type',
324
+ severity: 'debug',
325
+ module: 'test-module'
326
+ });
327
+
328
+ apos.testModule.logDebug(apos.task.getReq({
329
+ originalUrl: '/module/test',
330
+ path: '/test',
331
+ method: 'GET',
332
+ ip: '1.2.3.4',
333
+ query: { foo: 'bar' }
334
+ }), 'event-type', 'some message', { foo: 'bar' });
335
+ assert.equal(savedArgs[0], 'test-module: event-type: some message');
336
+ assert.deepEqual(savedArgs[1], {
337
+ url: '/module/test',
338
+ path: '/test',
339
+ method: 'GET',
340
+ ip: '1.2.3.4',
341
+ query: { foo: 'bar' },
342
+ requestId: 'test-id',
343
+ type: 'event-type',
344
+ severity: 'debug',
345
+ module: 'test-module',
346
+ foo: 'bar'
347
+ });
348
+
349
+ apos.testModule.logDebug(apos.task.getReq({
350
+ originalUrl: '/module/test',
351
+ path: '/test',
352
+ method: 'GET',
353
+ ip: '1.2.3.4',
354
+ query: { foo: 'bar' }
355
+ }), 'event-type', { foo: 'bar' });
356
+ assert.equal(savedArgs[0], 'test-module: event-type');
357
+ assert.deepEqual(savedArgs[1], {
358
+ url: '/module/test',
359
+ path: '/test',
360
+ method: 'GET',
361
+ ip: '1.2.3.4',
362
+ query: { foo: 'bar' },
363
+ requestId: 'test-id',
364
+ type: 'event-type',
365
+ severity: 'debug',
366
+ module: 'test-module',
367
+ foo: 'bar'
368
+ });
369
+
370
+ apos.util.logger.debug = debug;
371
+ apos.util.generateId = id;
372
+ });
373
+
374
+ it('should log formatted entry: logInfo', function () {
375
+ // id spy
376
+ const id = apos.util.generateId;
377
+ apos.util.generateId = () => 'test-id';
378
+
379
+ // debug spy
380
+ const info = apos.util.logger.info;
381
+ let savedArgs = [];
382
+ apos.util.logger.info = (...args) => {
383
+ savedArgs = args;
384
+ };
385
+
386
+ // Validate
387
+ assert.throws(() => {
388
+ apos.testModule.logInfo();
389
+ }, function (err) {
390
+ assert.equal(err.message, 'Event type must be a string');
391
+ return true;
392
+ });
393
+
394
+ // Format
395
+ apos.testModule.logInfo('event-type', 'a message', { foo: 'bar' });
396
+ assert.equal(savedArgs[0], 'test-module: event-type: a message');
397
+ assert.deepEqual(savedArgs[1], {
398
+ type: 'event-type',
399
+ severity: 'info',
400
+ module: 'test-module',
401
+ foo: 'bar'
402
+ });
403
+
404
+ apos.testModule.logInfo(apos.task.getReq({
405
+ originalUrl: '/module/test',
406
+ path: '/test',
407
+ method: 'GET',
408
+ ip: '1.2.3.4',
409
+ query: { foo: 'bar' }
410
+ }), 'event-type', 'some message', { foo: 'bar' });
411
+ assert.equal(savedArgs[0], 'test-module: event-type: some message');
412
+ assert.deepEqual(savedArgs[1], {
413
+ url: '/module/test',
414
+ path: '/test',
415
+ method: 'GET',
416
+ ip: '1.2.3.4',
417
+ query: { foo: 'bar' },
418
+ requestId: 'test-id',
419
+ type: 'event-type',
420
+ severity: 'info',
421
+ module: 'test-module',
422
+ foo: 'bar'
423
+ });
424
+
425
+ apos.util.logger.info = info;
426
+ apos.util.generateId = id;
427
+ });
428
+
429
+ it('should log formatted entry: logWarn', function () {
430
+ // id spy
431
+ const id = apos.util.generateId;
432
+ apos.util.generateId = () => 'test-id';
433
+
434
+ // debug spy
435
+ const warn = apos.util.logger.warn;
436
+ let savedArgs = [];
437
+ apos.util.logger.warn = (...args) => {
438
+ savedArgs = args;
439
+ };
440
+
441
+ // Validate
442
+ assert.throws(() => {
443
+ apos.testModule.logWarn();
444
+ }, function (err) {
445
+ assert.equal(err.message, 'Event type must be a string');
446
+ return true;
447
+ });
448
+
449
+ // Format
450
+ apos.testModule.logWarn('event-type', 'a message', { foo: 'bar' });
451
+ assert.equal(savedArgs[0], 'test-module: event-type: a message');
452
+ assert.deepEqual(savedArgs[1], {
453
+ type: 'event-type',
454
+ severity: 'warn',
455
+ module: 'test-module',
456
+ foo: 'bar'
457
+ });
458
+
459
+ apos.testModule.logWarn(apos.task.getReq({
460
+ originalUrl: '/module/test',
461
+ path: '/test',
462
+ method: 'GET',
463
+ ip: '1.2.3.4',
464
+ query: { foo: 'bar' }
465
+ }), 'event-type', 'some message', { foo: 'bar' });
466
+ assert.equal(savedArgs[0], 'test-module: event-type: some message');
467
+ assert.deepEqual(savedArgs[1], {
468
+ url: '/module/test',
469
+ path: '/test',
470
+ method: 'GET',
471
+ ip: '1.2.3.4',
472
+ query: { foo: 'bar' },
473
+ requestId: 'test-id',
474
+ type: 'event-type',
475
+ severity: 'warn',
476
+ module: 'test-module',
477
+ foo: 'bar'
478
+ });
479
+
480
+ apos.util.logger.warn = warn;
481
+ apos.util.generateId = id;
482
+ });
483
+
484
+ it('should log formatted entry: logError', function () {
485
+ // id spy
486
+ const id = apos.util.generateId;
487
+ apos.util.generateId = () => 'test-id';
488
+
489
+ // debug spy
490
+ const error = apos.util.logger.error;
491
+ let savedArgs = [];
492
+ apos.util.logger.error = (...args) => {
493
+ savedArgs = args;
494
+ };
495
+
496
+ // Validate
497
+ assert.throws(() => {
498
+ apos.testModule.logError();
499
+ }, function (err) {
500
+ assert.equal(err.message, 'Event type must be a string');
501
+ return true;
502
+ });
503
+
504
+ // Format
505
+ apos.testModule.logError('event-type', 'a message', { foo: 'bar' });
506
+ assert.equal(savedArgs[0], 'test-module: event-type: a message');
507
+ assert.deepEqual(savedArgs[1], {
508
+ type: 'event-type',
509
+ severity: 'error',
510
+ module: 'test-module',
511
+ foo: 'bar'
512
+ });
513
+
514
+ apos.testModule.logError(apos.task.getReq({
515
+ originalUrl: '/module/test',
516
+ path: '/test',
517
+ method: 'GET',
518
+ ip: '1.2.3.4',
519
+ query: { foo: 'bar' }
520
+ }), 'event-type', 'some message', { foo: 'bar' });
521
+ assert.equal(savedArgs[0], 'test-module: event-type: some message');
522
+ assert.deepEqual(savedArgs[1], {
523
+ url: '/module/test',
524
+ path: '/test',
525
+ method: 'GET',
526
+ ip: '1.2.3.4',
527
+ query: { foo: 'bar' },
528
+ requestId: 'test-id',
529
+ type: 'event-type',
530
+ severity: 'error',
531
+ module: 'test-module',
532
+ foo: 'bar'
533
+ });
534
+
535
+ apos.util.logger.error = error;
536
+ apos.util.generateId = id;
537
+ });
538
+
539
+ it('should filter entries with minimal config', async function () {
540
+ await t.destroy(apos);
541
+ apos = await t.create({
542
+ modules: {
543
+ ...testModule,
544
+ '@apostrophecms/log': {
545
+ options: {
546
+ filter: {
547
+ '*': {
548
+ severity: [ 'error' ]
549
+ },
550
+ 'test-module': {
551
+ events: [ 'type1' ]
552
+ }
553
+ }
554
+ }
555
+ }
556
+ }
557
+ });
558
+
559
+ assert.deepEqual(apos.structuredLog.filters, {
560
+ '*': {
561
+ severity: [ 'error' ]
562
+ },
563
+ 'test-module': {
564
+ events: [ 'type1' ]
565
+ }
566
+ });
567
+
568
+ let savedArgs = [];
569
+ const debug = apos.util.logger.debug;
570
+ apos.util.logger.debug = (...args) => {
571
+ savedArgs = args;
572
+ };
573
+ const info = apos.util.logger.info;
574
+ apos.util.logger.info = (...args) => {
575
+ savedArgs = args;
576
+ };
577
+ const warn = apos.util.logger.warn;
578
+ apos.util.logger.warn = (...args) => {
579
+ savedArgs = args;
580
+ };
581
+ const error = apos.util.logger.error;
582
+ apos.util.logger.error = (...args) => {
583
+ savedArgs = args;
584
+ };
585
+
586
+ // ### DEBUG
587
+ // No match
588
+ apos.global.logDebug('type2');
589
+ assert.equal(savedArgs.length, 0);
590
+
591
+ // No match - type from another module
592
+ savedArgs = [];
593
+ apos.global.logDebug('type1');
594
+ assert.equal(savedArgs.length, 0);
595
+
596
+ // Matches the global severity
597
+ savedArgs = [];
598
+ apos.global.logError('type1');
599
+ assert.equal(savedArgs.length, 2);
600
+ savedArgs = [];
601
+ apos.global.logError('type2');
602
+ assert.equal(savedArgs.length, 2);
603
+
604
+ // Matches the module type only
605
+ savedArgs = [];
606
+ apos.testModule.logDebug('type1');
607
+ assert.equal(savedArgs.length, 2);
608
+
609
+ // Matches the global severity and module type
610
+ savedArgs = [];
611
+ apos.testModule.logError('type1');
612
+ assert.equal(savedArgs.length, 2);
613
+
614
+ // Matches the global severity only
615
+ savedArgs = [];
616
+ apos.testModule.logError('type2');
617
+ assert.equal(savedArgs.length, 2);
618
+
619
+ // No match
620
+ savedArgs = [];
621
+ apos.testModule.logWarn('type3');
622
+ assert.equal(savedArgs.length, 0);
623
+
624
+ apos.util.logger.debug = debug;
625
+ apos.util.logger.info = info;
626
+ apos.util.logger.warn = warn;
627
+ apos.util.logger.error = error;
628
+ });
629
+
630
+ it('should match all with wildcard global config', async function () {
631
+ await t.destroy(apos);
632
+ apos = await t.create({
633
+ modules: {
634
+ ...testModule,
635
+ '@apostrophecms/log': {
636
+ options: {
637
+ filter: {
638
+ '*': true
639
+ }
640
+ }
641
+ }
642
+ }
643
+ });
644
+
645
+ assert.deepEqual(apos.structuredLog.filters, {
646
+ '*': {
647
+ severity: [ 'debug', 'info', 'warn', 'error' ]
648
+ }
649
+ });
650
+
651
+ let savedArgs = [];
652
+ const debug = apos.util.logger.debug;
653
+ apos.util.logger.debug = (...args) => {
654
+ savedArgs = args;
655
+ };
656
+ const info = apos.util.logger.info;
657
+ apos.util.logger.info = (...args) => {
658
+ savedArgs = args;
659
+ };
660
+ const warn = apos.util.logger.warn;
661
+ apos.util.logger.warn = (...args) => {
662
+ savedArgs = args;
663
+ };
664
+ const error = apos.util.logger.error;
665
+ apos.util.logger.error = (...args) => {
666
+ savedArgs = args;
667
+ };
668
+
669
+ // ### DEBUG
670
+ savedArgs = [];
671
+ apos.testModule.logDebug('type1');
672
+ assert.equal(savedArgs.length, 2);
673
+ savedArgs = [];
674
+ apos.testModule.logInfo('type1');
675
+ assert.equal(savedArgs.length, 2);
676
+ apos.testModule.logWarn('type1');
677
+ assert.equal(savedArgs.length, 2);
678
+ apos.testModule.logError('type1');
679
+ assert.equal(savedArgs.length, 2);
680
+
681
+ apos.util.logger.debug = debug;
682
+ apos.util.logger.info = info;
683
+ apos.util.logger.warn = warn;
684
+ apos.util.logger.error = error;
685
+ });
686
+
687
+ it('should filter entries with wildcard module events config', async function () {
688
+ await t.destroy(apos);
689
+ apos = await t.create({
690
+ modules: {
691
+ ...testModule,
692
+ '@apostrophecms/log': {
693
+ options: {
694
+ filter: {
695
+ '*': {
696
+ severity: [ 'error' ],
697
+ events: [ 'type1' ]
698
+ },
699
+ 'test-module': {
700
+ events: '*'
701
+ }
702
+ }
703
+ }
704
+ }
705
+ }
706
+ });
707
+
708
+ assert.deepEqual(apos.structuredLog.filters, {
709
+ '*': {
710
+ severity: [ 'error' ],
711
+ events: [ 'type1' ]
712
+ },
713
+ 'test-module': {
714
+ events: [ '*' ]
715
+ }
716
+ });
717
+
718
+ let savedArgs = [];
719
+ const debug = apos.util.logger.debug;
720
+ apos.util.logger.debug = (...args) => {
721
+ savedArgs = args;
722
+ };
723
+ const info = apos.util.logger.info;
724
+ apos.util.logger.info = (...args) => {
725
+ savedArgs = args;
726
+ };
727
+ const warn = apos.util.logger.warn;
728
+ apos.util.logger.warn = (...args) => {
729
+ savedArgs = args;
730
+ };
731
+ const error = apos.util.logger.error;
732
+ apos.util.logger.error = (...args) => {
733
+ savedArgs = args;
734
+ };
735
+
736
+ // ### DEBUG
737
+ savedArgs = [];
738
+ // Match the global type only
739
+ apos.global.logDebug('type1');
740
+ assert.equal(savedArgs.length, 2);
741
+
742
+ savedArgs = [];
743
+ // No match
744
+ apos.global.logDebug('type2');
745
+ assert.equal(savedArgs.length, 0);
746
+
747
+ // Always match because of the event type wildcard
748
+ savedArgs = [];
749
+ apos.testModule.logDebug('any-type');
750
+ assert.equal(savedArgs.length, 2);
751
+ savedArgs = [];
752
+ apos.testModule.logInfo('any-type');
753
+ assert.equal(savedArgs.length, 2);
754
+ apos.testModule.logWarn('any-type');
755
+ assert.equal(savedArgs.length, 2);
756
+ apos.testModule.logError('any-type');
757
+ assert.equal(savedArgs.length, 2);
758
+
759
+ apos.util.logger.debug = debug;
760
+ apos.util.logger.info = info;
761
+ apos.util.logger.warn = warn;
762
+ apos.util.logger.error = error;
763
+ });
764
+
765
+ it('should filter entries with verbose config', async function () {
766
+ await t.destroy(apos);
767
+ apos = await t.create({
768
+ modules: {
769
+ ...testModule,
770
+ '@apostrophecms/log': {
771
+ options: {
772
+ filter: {
773
+ '*': {
774
+ severity: [ 'error' ],
775
+ events: [ 'type1' ]
776
+ },
777
+ 'test-module': {
778
+ severity: [ 'debug' ],
779
+ events: [ 'type2' ]
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+ });
786
+
787
+ assert.deepEqual(apos.structuredLog.filters, {
788
+ '*': {
789
+ severity: [ 'error' ],
790
+ events: [ 'type1' ]
791
+ },
792
+ 'test-module': {
793
+ severity: [ 'debug' ],
794
+ events: [ 'type2' ]
795
+ }
796
+ });
797
+
798
+ let savedArgs = [];
799
+ const debug = apos.util.logger.debug;
800
+ apos.util.logger.debug = (...args) => {
801
+ savedArgs = args;
802
+ };
803
+ const info = apos.util.logger.info;
804
+ apos.util.logger.info = (...args) => {
805
+ savedArgs = args;
806
+ };
807
+ const warn = apos.util.logger.warn;
808
+ apos.util.logger.warn = (...args) => {
809
+ savedArgs = args;
810
+ };
811
+ const error = apos.util.logger.error;
812
+ apos.util.logger.error = (...args) => {
813
+ savedArgs = args;
814
+ };
815
+
816
+ // ### DEBUG
817
+ // No match
818
+ apos.global.logDebug('type2');
819
+ assert.equal(savedArgs.length, 0);
820
+
821
+ // Matches the type only
822
+ savedArgs = [];
823
+ apos.global.logDebug('type1');
824
+ assert.equal(savedArgs.length, 2);
825
+
826
+ // Matches the type and severity
827
+ savedArgs = [];
828
+ apos.global.logError('type1');
829
+ assert.equal(savedArgs.length, 2);
830
+
831
+ // Matches the global type and module severity
832
+ savedArgs = [];
833
+ apos.testModule.logDebug('type1');
834
+ assert.equal(savedArgs.length, 2);
835
+
836
+ // Matches the global type and severity
837
+ savedArgs = [];
838
+ apos.testModule.logError('type1');
839
+ assert.equal(savedArgs.length, 2);
840
+
841
+ // Matches the global type only
842
+ savedArgs = [];
843
+ apos.testModule.logInfo('type1');
844
+ assert.equal(savedArgs.length, 2);
845
+
846
+ // Matches the module type only
847
+ savedArgs = [];
848
+ apos.testModule.logInfo('type2');
849
+ assert.equal(savedArgs.length, 2);
850
+
851
+ // No match
852
+ savedArgs = [];
853
+ apos.testModule.logWarn('type3');
854
+ assert.equal(savedArgs.length, 0);
855
+
856
+ apos.util.logger.debug = debug;
857
+ apos.util.logger.info = info;
858
+ apos.util.logger.warn = warn;
859
+ apos.util.logger.error = error;
860
+ });
861
+
862
+ it('it should shutdown logger', async function () {
863
+ await t.destroy(apos);
864
+ apos = await t.create({});
865
+
866
+ let called = false;
867
+ apos.util.logger.destroy = async () => {
868
+ called = true;
869
+ };
870
+ await t.destroy(apos);
871
+ apos = null;
872
+
873
+ assert.equal(called, true);
874
+ });
875
+ });
876
+
877
+ describe('production', function () {
878
+ before(async function () {
879
+ process.env.NODE_ENV = 'production';
880
+ apos = await t.create({
881
+ modules: { ...testModule }
882
+ });
883
+ });
884
+
885
+ after(async function () {
886
+ delete process.env.NODE_ENV;
887
+ await t.destroy(apos);
888
+ apos = null;
889
+ });
890
+
891
+ it('should set filter configuration', function () {
892
+ assert.deepEqual(apos.structuredLog.filters, {
893
+ '*': { severity: [ 'warn', 'error' ] }
894
+ });
895
+ });
896
+
897
+ it('should filter and format entries', function () {
898
+ // id spy
899
+ const id = apos.util.generateId;
900
+ apos.util.generateId = () => 'test-id';
901
+ let savedArgs = [];
902
+
903
+ // ### DEBUG
904
+ const debug = console.debug;
905
+ console.debug = (...args) => {
906
+ savedArgs = args;
907
+ };
908
+
909
+ // Validate formatting
910
+ savedArgs = [];
911
+ apos.testModule.logDebug('event-type');
912
+ assert.equal(typeof savedArgs[0], 'undefined');
913
+ assert.equal(typeof savedArgs[1], 'undefined');
914
+
915
+ // Message as
916
+ savedArgs = [];
917
+ apos.structuredLog.options.messageAs = 'msg';
918
+ apos.testModule.logDebug('event-type', 'some message');
919
+ assert.equal(typeof savedArgs[0], 'undefined');
920
+ assert.equal(typeof savedArgs[1], 'undefined');
921
+ delete apos.structuredLog.options.messageAs;
922
+
923
+ // ### INFO
924
+ const info = console.info;
925
+ console.info = (...args) => {
926
+ savedArgs = args;
927
+ };
928
+
929
+ // Validate formatting
930
+ savedArgs = [];
931
+ apos.testModule.logInfo('event-type');
932
+ assert.equal(typeof savedArgs[0], 'undefined');
933
+ assert.equal(typeof savedArgs[1], 'undefined');
934
+
935
+ // ### WARN
936
+ const warn = console.warn;
937
+ console.warn = (...args) => {
938
+ savedArgs = args;
939
+ };
940
+
941
+ // Validate formatting
942
+ savedArgs = [];
943
+ apos.testModule.logWarn('event-type');
944
+ assert.equal(savedArgs[0], 'test-module: event-type');
945
+ assert.equal(
946
+ savedArgs[1],
947
+ '{"module":"test-module","type":"event-type","severity":"warn"}'
948
+ );
949
+
950
+ // ### ERROR
951
+ const error = console.error;
952
+ console.error = (...args) => {
953
+ savedArgs = args;
954
+ };
955
+
956
+ // Validate formatting
957
+ savedArgs = [];
958
+ apos.testModule.logError('event-type');
959
+ assert.equal(savedArgs[0], 'test-module: event-type');
960
+ assert.equal(
961
+ savedArgs[1],
962
+ '{"module":"test-module","type":"event-type","severity":"error"}'
963
+ );
964
+
965
+ apos.util.generateId = id;
966
+ console.debug = debug;
967
+ console.info = info;
968
+ console.warn = warn;
969
+ console.error = error;
970
+ });
971
+
972
+ it('should override default filter configuration', async function () {
973
+ await t.destroy(apos);
974
+ apos = await t.create({
975
+ modules: {
976
+ ...testModule,
977
+ '@apostrophecms/log': {
978
+ options: {
979
+ filter: {
980
+ '*': { severity: [ 'info', 'warn', 'error' ] }
981
+ }
982
+ }
983
+ }
984
+ }
985
+ });
986
+
987
+ assert.deepEqual(apos.structuredLog.filters, {
988
+ '*': { severity: [ 'info', 'warn', 'error' ] }
989
+ });
990
+ });
991
+ });
992
+
993
+ describe('APOS_FILTER_LOGS', function () {
994
+ beforeEach(async function () {
995
+ await t.destroy(apos);
996
+ apos = null;
997
+ });
998
+
999
+ after(async function () {
1000
+ delete process.env.APOS_FILTER_LOGS;
1001
+ await t.destroy(apos);
1002
+ apos = null;
1003
+ });
1004
+
1005
+ it('should override default filter configuration (wildcard)', async function () {
1006
+ process.env.APOS_FILTER_LOGS = '*';
1007
+ apos = await t.create({
1008
+ modules: {
1009
+ ...testModule,
1010
+ '@apostrophecms/log': {
1011
+ options: {
1012
+ filter: {
1013
+ '*': { severity: [ 'info', 'warn', 'error' ] },
1014
+ 'test-module': { events: [ 'type1' ] }
1015
+ }
1016
+ }
1017
+ }
1018
+ }
1019
+ });
1020
+
1021
+ assert.deepEqual(apos.structuredLog.filters, {
1022
+ '*': {
1023
+ severity: [ 'debug', 'info', 'warn', 'error' ]
1024
+ }
1025
+ });
1026
+ });
1027
+
1028
+ it('should override filter configuration via env', async function () {
1029
+ process.env.APOS_FILTER_LOGS = '*:severity:warn,error;test-module:events:type1,type2:severity:info';
1030
+ apos = await t.create({
1031
+ modules: {
1032
+ ...testModule,
1033
+ '@apostrophecms/log': {
1034
+ options: {
1035
+ filter: {
1036
+ '*': { severity: [ 'info', 'warn', 'error' ] },
1037
+ 'test-module': { events: [ 'type3' ] }
1038
+ }
1039
+ }
1040
+ }
1041
+ }
1042
+ });
1043
+
1044
+ assert.deepEqual(apos.structuredLog.filters, {
1045
+ '*': {
1046
+ severity: [ 'warn', 'error' ]
1047
+ },
1048
+ 'test-module': {
1049
+ severity: [ 'info' ],
1050
+ events: [ 'type1', 'type2' ]
1051
+ }
1052
+ });
1053
+ });
1054
+ });
1055
+
1056
+ describe('legacy logging with :messageAs"', function () {
1057
+ before(async function () {
1058
+ await t.destroy(apos);
1059
+ apos = await t.create({
1060
+ modules: {
1061
+ '@apostrophecms/log': {
1062
+ options: {
1063
+ messageAs: 'msg'
1064
+ }
1065
+ }
1066
+ }
1067
+ });
1068
+ });
1069
+
1070
+ after(async function () {
1071
+ delete process.env.APOS_FILTER_LOGS;
1072
+ await t.destroy(apos);
1073
+ apos = null;
1074
+ });
1075
+
1076
+ it('should log object: debug', function () {
1077
+ let savedArgs = [];
1078
+ const saved = apos.util.logger.debug;
1079
+ apos.util.logger.debug = (...args) => {
1080
+ savedArgs = args;
1081
+ };
1082
+
1083
+ savedArgs = [];
1084
+ apos.util.debug('some message');
1085
+ assert.deepEqual(savedArgs, [ { msg: 'some message' } ]);
1086
+
1087
+ savedArgs = [];
1088
+ apos.util.debug({ foo: 'bar' });
1089
+ assert.deepEqual(savedArgs, [ { foo: 'bar' } ]);
1090
+
1091
+ savedArgs = [];
1092
+ apos.util.debug('some message', { foo: 'bar' });
1093
+ assert.deepEqual(savedArgs, [ {
1094
+ foo: 'bar',
1095
+ msg: 'some message'
1096
+ } ]);
1097
+
1098
+ savedArgs = [];
1099
+ apos.util.debug('some message', 'more', { foo: 'bar' });
1100
+ assert.deepEqual(savedArgs, [ {
1101
+ foo: 'bar',
1102
+ msg: 'some message',
1103
+ args: [ 'more' ]
1104
+ } ]);
1105
+
1106
+ savedArgs = [];
1107
+ apos.util.debug({ foo: 'bar' }, 'some message', 'more');
1108
+ assert.deepEqual(savedArgs, [ {
1109
+ foo: 'bar',
1110
+ msg: 'some message',
1111
+ args: [ 'more' ]
1112
+ } ]);
1113
+
1114
+ apos.util.logger.debug = saved;
1115
+ });
1116
+
1117
+ it('should log object: log', function () {
1118
+ let savedArgs = [];
1119
+ const saved = apos.util.logger.log;
1120
+ apos.util.logger.log = (...args) => {
1121
+ savedArgs = args;
1122
+ };
1123
+
1124
+ savedArgs = [];
1125
+ apos.util.log('some message');
1126
+ assert.deepEqual(savedArgs, [ { msg: 'some message' } ]);
1127
+
1128
+ savedArgs = [];
1129
+ apos.util.log({ foo: 'bar' });
1130
+ assert.deepEqual(savedArgs, [ { foo: 'bar' } ]);
1131
+
1132
+ savedArgs = [];
1133
+ apos.util.log('some message', { foo: 'bar' });
1134
+ assert.deepEqual(savedArgs, [ {
1135
+ foo: 'bar',
1136
+ msg: 'some message'
1137
+ } ]);
1138
+
1139
+ savedArgs = [];
1140
+ apos.util.log('some message', 'more', { foo: 'bar' });
1141
+ assert.deepEqual(savedArgs, [ {
1142
+ foo: 'bar',
1143
+ msg: 'some message',
1144
+ args: [ 'more' ]
1145
+ } ]);
1146
+
1147
+ savedArgs = [];
1148
+ apos.util.log({ foo: 'bar' }, 'some message', 'more');
1149
+ assert.deepEqual(savedArgs, [ {
1150
+ foo: 'bar',
1151
+ msg: 'some message',
1152
+ args: [ 'more' ]
1153
+ } ]);
1154
+
1155
+ apos.util.logger.log = saved;
1156
+ });
1157
+
1158
+ it('should log object: info', function () {
1159
+ let savedArgs = [];
1160
+ const saved = apos.util.logger.info;
1161
+ apos.util.logger.info = (...args) => {
1162
+ savedArgs = args;
1163
+ };
1164
+
1165
+ savedArgs = [];
1166
+ apos.util.info('some message');
1167
+ assert.deepEqual(savedArgs, [ { msg: 'some message' } ]);
1168
+
1169
+ savedArgs = [];
1170
+ apos.util.info({ foo: 'bar' });
1171
+ assert.deepEqual(savedArgs, [ { foo: 'bar' } ]);
1172
+
1173
+ savedArgs = [];
1174
+ apos.util.info('some message', { foo: 'bar' });
1175
+ assert.deepEqual(savedArgs, [ {
1176
+ foo: 'bar',
1177
+ msg: 'some message'
1178
+ } ]);
1179
+
1180
+ savedArgs = [];
1181
+ apos.util.info('some message', 'more', { foo: 'bar' });
1182
+ assert.deepEqual(savedArgs, [ {
1183
+ foo: 'bar',
1184
+ msg: 'some message',
1185
+ args: [ 'more' ]
1186
+ } ]);
1187
+
1188
+ savedArgs = [];
1189
+ apos.util.info({ foo: 'bar' }, 'some message', 'more');
1190
+ assert.deepEqual(savedArgs, [ {
1191
+ foo: 'bar',
1192
+ msg: 'some message',
1193
+ args: [ 'more' ]
1194
+ } ]);
1195
+
1196
+ apos.util.logger.info = saved;
1197
+ });
1198
+
1199
+ it('should log object: warn', function () {
1200
+ let savedArgs = [];
1201
+ const saved = apos.util.logger.warn;
1202
+ apos.util.logger.warn = (...args) => {
1203
+ savedArgs = args;
1204
+ };
1205
+
1206
+ savedArgs = [];
1207
+ apos.util.warn('some message');
1208
+ assert.deepEqual(savedArgs, [ { msg: 'some message' } ]);
1209
+
1210
+ savedArgs = [];
1211
+ apos.util.warn({ foo: 'bar' });
1212
+ assert.deepEqual(savedArgs, [ { foo: 'bar' } ]);
1213
+
1214
+ savedArgs = [];
1215
+ apos.util.warn('some message', { foo: 'bar' });
1216
+ assert.deepEqual(savedArgs, [ {
1217
+ foo: 'bar',
1218
+ msg: 'some message'
1219
+ } ]);
1220
+
1221
+ savedArgs = [];
1222
+ apos.util.warn('some message', 'more', { foo: 'bar' });
1223
+ assert.deepEqual(savedArgs, [ {
1224
+ foo: 'bar',
1225
+ msg: 'some message',
1226
+ args: [ 'more' ]
1227
+ } ]);
1228
+
1229
+ savedArgs = [];
1230
+ apos.util.warn({ foo: 'bar' }, 'some message', 'more');
1231
+ assert.deepEqual(savedArgs, [ {
1232
+ foo: 'bar',
1233
+ msg: 'some message',
1234
+ args: [ 'more' ]
1235
+ } ]);
1236
+
1237
+ apos.util.logger.warn = saved;
1238
+ });
1239
+
1240
+ it('should log object: error', function () {
1241
+ let savedArgs = [];
1242
+ const saved = apos.util.logger.error;
1243
+ apos.util.logger.error = (...args) => {
1244
+ savedArgs = args;
1245
+ };
1246
+
1247
+ savedArgs = [];
1248
+ apos.util.error('some message');
1249
+ assert.deepEqual(savedArgs, [ { msg: 'some message' } ]);
1250
+
1251
+ savedArgs = [];
1252
+ apos.util.error({ foo: 'bar' });
1253
+ assert.deepEqual(savedArgs, [ { foo: 'bar' } ]);
1254
+
1255
+ savedArgs = [];
1256
+ apos.util.error('some message', { foo: 'bar' });
1257
+ assert.deepEqual(savedArgs, [ {
1258
+ foo: 'bar',
1259
+ msg: 'some message'
1260
+ } ]);
1261
+
1262
+ savedArgs = [];
1263
+ apos.util.error('some message', 'more', { foo: 'bar' });
1264
+ assert.deepEqual(savedArgs, [ {
1265
+ foo: 'bar',
1266
+ msg: 'some message',
1267
+ args: [ 'more' ]
1268
+ } ]);
1269
+
1270
+ savedArgs = [];
1271
+ apos.util.error({ foo: 'bar' }, 'some message', 'more');
1272
+ assert.deepEqual(savedArgs, [ {
1273
+ foo: 'bar',
1274
+ msg: 'some message',
1275
+ args: [ 'more' ]
1276
+ } ]);
1277
+
1278
+ apos.util.logger.error = saved;
1279
+ });
1280
+
1281
+ it('should log object: warnDev', function () {
1282
+ let savedArgs = [];
1283
+ const saved = apos.util.logger.warn;
1284
+ apos.util.logger.warn = (...args) => {
1285
+ savedArgs = args;
1286
+ };
1287
+
1288
+ savedArgs = [];
1289
+ apos.util.warnDev('some message');
1290
+ assert.deepEqual(savedArgs, [ {
1291
+ msg: 'some message',
1292
+ args: [ '\n⚠️ ', '\n' ]
1293
+ } ]);
1294
+
1295
+ savedArgs = [];
1296
+ apos.util.warnDev({ foo: 'bar' });
1297
+ assert.deepEqual(savedArgs, [ {
1298
+ foo: 'bar',
1299
+ args: [ '\n⚠️ ', '\n' ]
1300
+ }
1301
+ ]);
1302
+
1303
+ savedArgs = [];
1304
+ apos.util.warnDev('some message', { foo: 'bar' });
1305
+ assert.deepEqual(savedArgs, [ {
1306
+ foo: 'bar',
1307
+ msg: 'some message',
1308
+ args: [ '\n⚠️ ', '\n' ]
1309
+ } ]);
1310
+
1311
+ savedArgs = [];
1312
+ apos.util.warnDev('some message', 'more', { foo: 'bar' });
1313
+ assert.deepEqual(savedArgs, [ {
1314
+ foo: 'bar',
1315
+ msg: 'some message',
1316
+ args: [ '\n⚠️ ', 'more', '\n' ]
1317
+ } ]);
1318
+
1319
+ savedArgs = [];
1320
+ apos.util.warnDev({ foo: 'bar' }, 'some message', 'more');
1321
+ assert.deepEqual(savedArgs, [ {
1322
+ foo: 'bar',
1323
+ msg: 'some message',
1324
+ args: [ '\n⚠️ ', 'more', '\n' ]
1325
+ } ]);
1326
+
1327
+ apos.util.logger.warn = saved;
1328
+ });
1329
+ });
1330
+
1331
+ describe('route error', function () {
1332
+ let user;
1333
+ let jar;
1334
+ let aposError;
1335
+ let consoleError;
1336
+ let generateId;
1337
+
1338
+ async function login() {
1339
+ // Create user and initialize session.
1340
+ if (!user) {
1341
+ user = await t.createAdmin(apos, {
1342
+ username: 'admin',
1343
+ password: 'admin'
1344
+ });
1345
+ }
1346
+ jar = await t.getUserJar(apos, user);
1347
+ await apos.http.get('/', { jar });
1348
+ }
1349
+
1350
+ before(async function () {
1351
+ await t.destroy(apos);
1352
+ apos = await t.create({
1353
+ modules: {
1354
+ 'test-piece': {
1355
+ extend: '@apostrophecms/piece-type',
1356
+ fields: {
1357
+ add: {
1358
+ field1: {
1359
+ type: 'string',
1360
+ label: 'Field1',
1361
+ required: true
1362
+ },
1363
+ field2: {
1364
+ type: 'string',
1365
+ label: 'Field2',
1366
+ required: true
1367
+ }
1368
+ }
1369
+ }
1370
+ },
1371
+ 'test-module': {
1372
+ apiRoutes(self) {
1373
+ return {
1374
+ post: {
1375
+ async conflict(req) {
1376
+ const err = self.apos.error(
1377
+ 'conflict',
1378
+ 'Conflict error',
1379
+ { some: 'data' }
1380
+ );
1381
+ err.path = 'some.field';
1382
+ throw err;
1383
+ }
1384
+ }
1385
+ };
1386
+ }
1387
+ }
1388
+ }
1389
+ });
1390
+ await login();
1391
+ aposError = apos.util.logger.error;
1392
+ generateId = apos.util.generateId;
1393
+ consoleError = console.error;
1394
+ });
1395
+
1396
+ beforeEach(async function () {
1397
+ apos.util.logger.error = aposError;
1398
+ apos.util.generateId = generateId;
1399
+ console.error = consoleError;
1400
+ });
1401
+
1402
+ after(async function () {
1403
+ await t.destroy(apos);
1404
+ apos = null;
1405
+ });
1406
+
1407
+ it('should log invalid error', async function () {
1408
+ apos.util.generateId = () => 'test-id';
1409
+ let savedArgs = [];
1410
+ apos.util.logger.error = (...args) => {
1411
+ savedArgs = args;
1412
+ };
1413
+ try {
1414
+ await apos.http.post('/api/v1/test-piece', {
1415
+ body: {},
1416
+ jar
1417
+ });
1418
+ } catch (e) {
1419
+ //
1420
+ }
1421
+ assert.equal(savedArgs[0], 'test-piece: api-error-invalid: invalid');
1422
+ assert.equal(savedArgs[1].module, 'test-piece');
1423
+ assert.equal(savedArgs[1].type, 'api-error-invalid');
1424
+ assert.equal(savedArgs[1].severity, 'error');
1425
+ assert.equal(savedArgs[1].url, '/api/v1/test-piece');
1426
+ assert.equal(savedArgs[1].path, '/api/v1/test-piece');
1427
+ assert.equal(savedArgs[1].method, 'POST');
1428
+ assert(savedArgs[1].ip);
1429
+ assert.deepEqual(savedArgs[1].query, {});
1430
+ assert.equal(savedArgs[1].requestId, 'test-id');
1431
+ assert.equal(savedArgs[1].name, 'invalid');
1432
+ assert.equal(Array.isArray(savedArgs[1].stack), true);
1433
+ assert.equal(savedArgs[1].errorPath, undefined);
1434
+ assert.deepEqual(savedArgs[1].data.errors, [
1435
+ {
1436
+ name: 'required',
1437
+ code: 422,
1438
+ message: 'required',
1439
+ data: {},
1440
+ path: 'title'
1441
+ },
1442
+ {
1443
+ name: 'required',
1444
+ code: 422,
1445
+ message: 'required',
1446
+ data: {},
1447
+ path: 'field1'
1448
+ },
1449
+ {
1450
+ name: 'required',
1451
+ code: 422,
1452
+ message: 'required',
1453
+ data: {},
1454
+ path: 'field2'
1455
+ }
1456
+ ]);
1457
+
1458
+ // Test the property order
1459
+ savedArgs = [];
1460
+ apos.util.logger.error = aposError;
1461
+ console.error = (...args) => {
1462
+ savedArgs = args;
1463
+ };
1464
+
1465
+ try {
1466
+ await apos.http.post('/api/v1/test-piece', {
1467
+ body: {},
1468
+ jar
1469
+ });
1470
+ } catch (e) {
1471
+ //
1472
+ }
1473
+ // Skip IP as it might get changed in CI
1474
+ assert.equal(savedArgs[0], 'test-piece: api-error-invalid: invalid\n');
1475
+ assert.equal(
1476
+ savedArgs[1].startsWith(
1477
+ `{
1478
+ "module": "test-piece",
1479
+ "type": "api-error-invalid",
1480
+ "severity": "error",
1481
+ "url": "/api/v1/test-piece",
1482
+ "path": "/api/v1/test-piece",
1483
+ "method": "POST",
1484
+ `
1485
+ ),
1486
+ true
1487
+ );
1488
+ assert.equal(
1489
+ savedArgs[1].includes(
1490
+ `
1491
+ "query": {},
1492
+ "requestId": "test-id",
1493
+ "name": "invalid",
1494
+ "status": 400,
1495
+ "stack": [
1496
+ `
1497
+ ),
1498
+ true
1499
+ );
1500
+ assert.equal(
1501
+ savedArgs[1].endsWith(
1502
+ `
1503
+ ],
1504
+ "data": {
1505
+ "errors": [
1506
+ {
1507
+ "name": "required",
1508
+ "code": 422,
1509
+ "message": "required",
1510
+ "data": {},
1511
+ "path": "title"
1512
+ },
1513
+ {
1514
+ "name": "required",
1515
+ "code": 422,
1516
+ "message": "required",
1517
+ "data": {},
1518
+ "path": "field1"
1519
+ },
1520
+ {
1521
+ "name": "required",
1522
+ "code": 422,
1523
+ "message": "required",
1524
+ "data": {},
1525
+ "path": "field2"
1526
+ }
1527
+ ]
1528
+ }
1529
+ }`
1530
+ ),
1531
+ true
1532
+ );
1533
+
1534
+ });
1535
+
1536
+ it('should log conflict error with data and custom message', async function () {
1537
+ apos.util.generateId = () => 'test-id';
1538
+ let savedArgs = [];
1539
+ apos.util.logger.error = (...args) => {
1540
+ savedArgs = args;
1541
+ };
1542
+ try {
1543
+ await apos.http.post('/api/v1/test-module/conflict', {
1544
+ qs: { foo: 'bar' },
1545
+ jar
1546
+ });
1547
+ } catch (e) {
1548
+ //
1549
+ }
1550
+ assert.equal(savedArgs[0], 'test-module: api-error-conflict: Conflict error');
1551
+ assert.equal(savedArgs[1].module, 'test-module');
1552
+ assert.equal(savedArgs[1].type, 'api-error-conflict');
1553
+ assert.equal(savedArgs[1].severity, 'error');
1554
+ assert.equal(savedArgs[1].url, '/api/v1/test-module/conflict?foo=bar');
1555
+ assert.equal(savedArgs[1].path, '/api/v1/test-module/conflict');
1556
+ assert.equal(savedArgs[1].method, 'POST');
1557
+ assert(savedArgs[1].ip);
1558
+ assert.deepEqual(savedArgs[1].query, { foo: 'bar' });
1559
+ assert.equal(savedArgs[1].requestId, 'test-id');
1560
+ assert.equal(savedArgs[1].name, 'conflict');
1561
+ assert.equal(Array.isArray(savedArgs[1].stack), true);
1562
+ assert.equal(savedArgs[1].errorPath, 'some.field');
1563
+ assert.deepEqual(savedArgs[1].data, { some: 'data' });
1564
+ });
1565
+ });
1566
+
1567
+ describe('login', function () {
1568
+ let user;
1569
+ let aposInfo;
1570
+
1571
+ async function createInstance() {
1572
+ apos = await t.create({
1573
+ modules: {
1574
+ '@apostrophecms/log': {
1575
+ options: {
1576
+ filter: {
1577
+ '*': {
1578
+ severity: [ 'info' ]
1579
+ },
1580
+ '@apostrophecms/login': {
1581
+ events: [
1582
+ 'incorrect-username',
1583
+ 'incorrect-password',
1584
+ 'correct-password',
1585
+ 'complete'
1586
+ ]
1587
+ }
1588
+ }
1589
+ }
1590
+ }
1591
+ }
1592
+ });
1593
+ aposInfo = apos.util.logger.info;
1594
+ user = await t.createAdmin(apos, {
1595
+ username: 'admin',
1596
+ password: 'admin'
1597
+ });
1598
+ user.password = 'admin';
1599
+ }
1600
+
1601
+ before(async function () {
1602
+ await t.destroy(apos);
1603
+ await createInstance();
1604
+ });
1605
+
1606
+ beforeEach(async function () {
1607
+ apos.util.logger.info = aposInfo;
1608
+ });
1609
+
1610
+ after(async function () {
1611
+ await t.destroy(apos);
1612
+ apos = null;
1613
+ });
1614
+
1615
+ it('should log incorrect username', async function () {
1616
+ let savedArgs = [];
1617
+ apos.util.logger.info = (...args) => {
1618
+ savedArgs = args;
1619
+ };
1620
+ const jar = apos.http.jar();
1621
+ try {
1622
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1623
+ body: {
1624
+ username: 'incorrect',
1625
+ password: user.password,
1626
+ session: true
1627
+ },
1628
+ jar
1629
+ });
1630
+ } catch (e) {
1631
+ //
1632
+ }
1633
+
1634
+ assert.equal(savedArgs[0], '@apostrophecms/login: incorrect-username');
1635
+ assert(savedArgs[1].ip);
1636
+ assert(savedArgs[1].requestId);
1637
+ delete savedArgs[1].ip;
1638
+ delete savedArgs[1].requestId;
1639
+ assert.deepEqual(savedArgs[1], {
1640
+ module: '@apostrophecms/login',
1641
+ type: 'incorrect-username',
1642
+ severity: 'info',
1643
+ username: 'incorrect',
1644
+ attempts: 1
1645
+ });
1646
+
1647
+ savedArgs = [];
1648
+ try {
1649
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1650
+ body: {
1651
+ username: 'incorrect',
1652
+ password: user.password,
1653
+ session: true
1654
+ },
1655
+ jar
1656
+ });
1657
+ } catch (e) {
1658
+ //
1659
+ }
1660
+ assert.equal(savedArgs[1].attempts, 2);
1661
+ });
1662
+
1663
+ it('should log incorrect password', async function () {
1664
+ await t.destroy(apos);
1665
+ await createInstance();
1666
+ let savedArgs = [];
1667
+ apos.util.logger.info = (...args) => {
1668
+ savedArgs = args;
1669
+ };
1670
+ const jar = apos.http.jar();
1671
+ try {
1672
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1673
+ body: {
1674
+ username: user.password,
1675
+ password: 'incorrect',
1676
+ session: true
1677
+ },
1678
+ jar
1679
+ });
1680
+ } catch (e) {
1681
+ //
1682
+ }
1683
+ assert.equal(savedArgs[0], '@apostrophecms/login: incorrect-password');
1684
+ assert(savedArgs[1].ip);
1685
+ assert(savedArgs[1].requestId);
1686
+ delete savedArgs[1].ip;
1687
+ delete savedArgs[1].requestId;
1688
+ assert.deepEqual(savedArgs[1], {
1689
+ module: '@apostrophecms/login',
1690
+ type: 'incorrect-password',
1691
+ severity: 'info',
1692
+ username: user.username,
1693
+ attempts: 1
1694
+ });
1695
+
1696
+ savedArgs = [];
1697
+ try {
1698
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1699
+ body: {
1700
+ username: user.password,
1701
+ password: 'incorrect',
1702
+ session: true
1703
+ },
1704
+ jar
1705
+ });
1706
+ } catch (e) {
1707
+ //
1708
+ }
1709
+ assert.equal(savedArgs[1].attempts, 2);
1710
+ });
1711
+
1712
+ it('should log login complete', async function () {
1713
+ await t.destroy(apos);
1714
+ await createInstance();
1715
+
1716
+ const jar = apos.http.jar();
1717
+ try {
1718
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1719
+ body: {
1720
+ username: user.password,
1721
+ password: 'incorrect',
1722
+ session: true
1723
+ },
1724
+ jar
1725
+ });
1726
+ } catch (e) {
1727
+ //
1728
+ }
1729
+
1730
+ let savedArgs = [];
1731
+ apos.util.logger.info = (...args) => {
1732
+ savedArgs = args;
1733
+ };
1734
+ savedArgs = [];
1735
+ try {
1736
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
1737
+ body: {
1738
+ username: user.password,
1739
+ password: user.password,
1740
+ session: true
1741
+ },
1742
+ jar
1743
+ });
1744
+ } catch (e) {
1745
+ //
1746
+ }
1747
+ assert.equal(savedArgs[0], '@apostrophecms/login: complete');
1748
+ assert(savedArgs[1].ip);
1749
+ assert(savedArgs[1].requestId);
1750
+ delete savedArgs[1].ip;
1751
+ delete savedArgs[1].requestId;
1752
+ assert.deepEqual(savedArgs[1], {
1753
+ module: '@apostrophecms/login',
1754
+ type: 'complete',
1755
+ severity: 'info',
1756
+ url: '/api/v1/@apostrophecms/login/login',
1757
+ path: '/api/v1/@apostrophecms/login/login',
1758
+ method: 'POST',
1759
+ query: {},
1760
+ username: 'admin',
1761
+ attempts: 1
1762
+ });
1763
+ });
1764
+ });
1765
+ });