directus 9.15.1 → 9.17.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 (107) hide show
  1. package/dist/__utils__/items-utils.d.ts +2 -0
  2. package/dist/__utils__/items-utils.js +36 -0
  3. package/dist/__utils__/schemas.d.ts +13 -0
  4. package/dist/__utils__/schemas.js +304 -0
  5. package/dist/__utils__/snapshots.d.ts +5 -0
  6. package/dist/__utils__/snapshots.js +897 -0
  7. package/dist/app.js +8 -7
  8. package/dist/auth/drivers/ldap.js +1 -0
  9. package/dist/auth/drivers/local.js +6 -0
  10. package/dist/auth/drivers/oauth2.js +1 -0
  11. package/dist/auth/drivers/openid.js +1 -0
  12. package/dist/cli/index.test.d.ts +1 -0
  13. package/dist/cli/index.test.js +58 -0
  14. package/dist/cli/utils/create-env/env-stub.liquid +6 -2
  15. package/dist/controllers/activity.js +1 -0
  16. package/dist/controllers/assets.js +20 -16
  17. package/dist/controllers/auth.js +6 -9
  18. package/dist/controllers/files.test.d.ts +1 -0
  19. package/dist/controllers/files.test.js +49 -0
  20. package/dist/controllers/server.js +0 -1
  21. package/dist/database/migrations/20220826A-add-origin-to-accountability.d.ts +3 -0
  22. package/dist/database/migrations/20220826A-add-origin-to-accountability.js +21 -0
  23. package/dist/database/migrations/run.test.d.ts +1 -0
  24. package/dist/database/migrations/run.test.js +92 -0
  25. package/dist/database/system-data/fields/activity.yaml +6 -0
  26. package/dist/database/system-data/fields/sessions.yaml +2 -0
  27. package/dist/env.js +15 -0
  28. package/dist/env.test.d.ts +8 -0
  29. package/dist/env.test.js +39 -0
  30. package/dist/extensions.d.ts +1 -0
  31. package/dist/extensions.js +16 -3
  32. package/dist/flows.js +28 -17
  33. package/dist/mailer.js +1 -0
  34. package/dist/middleware/authenticate.d.ts +1 -1
  35. package/dist/middleware/authenticate.js +1 -0
  36. package/dist/middleware/authenticate.test.d.ts +1 -0
  37. package/dist/middleware/authenticate.test.js +214 -0
  38. package/dist/middleware/extract-token.test.d.ts +1 -0
  39. package/dist/middleware/extract-token.test.js +60 -0
  40. package/dist/middleware/validate-batch.d.ts +1 -2
  41. package/dist/middleware/validate-batch.js +10 -13
  42. package/dist/middleware/validate-batch.test.d.ts +1 -0
  43. package/dist/middleware/validate-batch.test.js +82 -0
  44. package/dist/operations/exec/index.d.ts +5 -0
  45. package/dist/operations/exec/index.js +26 -0
  46. package/dist/operations/exec/index.test.d.ts +1 -0
  47. package/dist/operations/exec/index.test.js +95 -0
  48. package/dist/operations/notification/index.js +9 -6
  49. package/dist/operations/request/index.js +22 -3
  50. package/dist/operations/transform/index.d.ts +1 -1
  51. package/dist/operations/transform/index.js +1 -1
  52. package/dist/services/authentication.js +13 -3
  53. package/dist/services/files.js +3 -2
  54. package/dist/services/files.test.d.ts +1 -0
  55. package/dist/services/files.test.js +53 -0
  56. package/dist/services/flows.js +4 -0
  57. package/dist/services/graphql/index.d.ts +2 -2
  58. package/dist/services/graphql/index.js +78 -75
  59. package/dist/services/items.js +98 -42
  60. package/dist/services/items.test.d.ts +1 -0
  61. package/dist/services/items.test.js +765 -0
  62. package/dist/services/payload.d.ts +7 -4
  63. package/dist/services/payload.js +63 -12
  64. package/dist/services/payload.test.d.ts +1 -0
  65. package/dist/services/payload.test.js +94 -0
  66. package/dist/services/server.js +10 -7
  67. package/dist/services/shares.js +2 -1
  68. package/dist/services/specifications.test.d.ts +1 -0
  69. package/dist/services/specifications.test.js +96 -0
  70. package/dist/types/items.d.ts +11 -0
  71. package/dist/utils/apply-query.js +7 -3
  72. package/dist/utils/apply-snapshot.js +15 -0
  73. package/dist/utils/apply-snapshot.test.d.ts +1 -0
  74. package/dist/utils/apply-snapshot.test.js +305 -0
  75. package/dist/utils/async-handler.d.ts +2 -6
  76. package/dist/utils/async-handler.js +1 -13
  77. package/dist/utils/async-handler.test.d.ts +1 -0
  78. package/dist/utils/async-handler.test.js +18 -0
  79. package/dist/utils/calculate-field-depth.test.d.ts +1 -0
  80. package/dist/utils/calculate-field-depth.test.js +76 -0
  81. package/dist/utils/filter-items.test.d.ts +1 -0
  82. package/dist/utils/filter-items.test.js +60 -0
  83. package/dist/utils/get-cache-key.test.d.ts +1 -0
  84. package/dist/utils/get-cache-key.test.js +53 -0
  85. package/dist/utils/get-column-path.test.d.ts +1 -0
  86. package/dist/utils/get-column-path.test.js +136 -0
  87. package/dist/utils/get-config-from-env.test.d.ts +1 -0
  88. package/dist/utils/get-config-from-env.test.js +19 -0
  89. package/dist/utils/get-graphql-type.d.ts +1 -1
  90. package/dist/utils/get-graphql-type.js +4 -1
  91. package/dist/utils/get-os-info.d.ts +9 -0
  92. package/dist/utils/get-os-info.js +47 -0
  93. package/dist/utils/get-relation-info.test.d.ts +1 -0
  94. package/dist/utils/get-relation-info.test.js +88 -0
  95. package/dist/utils/get-relation-type.test.d.ts +1 -0
  96. package/dist/utils/get-relation-type.test.js +69 -0
  97. package/dist/utils/get-string-byte-size.test.d.ts +1 -0
  98. package/dist/utils/get-string-byte-size.test.js +8 -0
  99. package/dist/utils/is-directus-jwt.test.d.ts +1 -0
  100. package/dist/utils/is-directus-jwt.test.js +26 -0
  101. package/dist/utils/jwt.test.d.ts +1 -0
  102. package/dist/utils/jwt.test.js +36 -0
  103. package/dist/utils/merge-permissions.test.d.ts +1 -0
  104. package/dist/utils/merge-permissions.test.js +80 -0
  105. package/dist/utils/validate-keys.test.d.ts +1 -0
  106. package/dist/utils/validate-keys.test.js +97 -0
  107. package/package.json +14 -12
@@ -52,6 +52,7 @@ class ItemsService {
52
52
  .filter((field) => field.alias === true)
53
53
  .map((field) => field.field);
54
54
  const payload = (0, lodash_1.cloneDeep)(data);
55
+ const nestedActionEvents = [];
55
56
  // By wrapping the logic in a transaction, we make sure we automatically roll back all the
56
57
  // changes in the DB if any of the parts contained within throws an error. This also means
57
58
  // that any errors thrown in any nested relational changes will bubble up and cancel the whole
@@ -84,8 +85,8 @@ class ItemsService {
84
85
  const payloadWithPresets = this.accountability
85
86
  ? await authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
86
87
  : payloadAfterHooks;
87
- const { payload: payloadWithM2O, revisions: revisionsM2O } = await payloadService.processM2O(payloadWithPresets);
88
- const { payload: payloadWithA2O, revisions: revisionsA2O } = await payloadService.processA2O(payloadWithM2O);
88
+ const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
89
+ const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
89
90
  const payloadWithoutAliases = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, ...aliases));
90
91
  const payloadWithTypeCasting = await payloadService.processValues('create', payloadWithoutAliases);
91
92
  // In case of manual string / UUID primary keys, the PK already exists in the object we're saving.
@@ -113,7 +114,10 @@ class ItemsService {
113
114
  // to read from it
114
115
  payload[primaryKeyField] = primaryKey;
115
116
  }
116
- const { revisions: revisionsO2M } = await payloadService.processO2M(payload, primaryKey);
117
+ const { revisions: revisionsO2M, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payload, primaryKey, opts);
118
+ nestedActionEvents.push(...nestedActionEventsM2O);
119
+ nestedActionEvents.push(...nestedActionEventsA2O);
120
+ nestedActionEvents.push(...nestedActionEventsO2M);
117
121
  // If this is an authenticated action, and accountability tracking is enabled, save activity row
118
122
  if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
119
123
  const activityService = new index_1.ActivityService({
@@ -126,6 +130,7 @@ class ItemsService {
126
130
  collection: this.collection,
127
131
  ip: this.accountability.ip,
128
132
  user_agent: this.accountability.userAgent,
133
+ origin: this.accountability.origin,
129
134
  item: primaryKey,
130
135
  });
131
136
  // If revisions are tracked, create revisions record
@@ -154,17 +159,30 @@ class ItemsService {
154
159
  return primaryKey;
155
160
  });
156
161
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
157
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.create', `${this.collection}.items.create`] : `${this.eventScope}.create`, {
158
- payload,
159
- key: primaryKey,
160
- collection: this.collection,
161
- }, {
162
- // This hook is called async. If we would pass the transaction here, the hook can be
163
- // called after the transaction is done #5460
164
- database: this.knex || (0, database_1.default)(),
165
- schema: this.schema,
166
- accountability: this.accountability,
167
- });
162
+ const actionEvent = {
163
+ event: this.eventScope === 'items'
164
+ ? ['items.create', `${this.collection}.items.create`]
165
+ : `${this.eventScope}.create`,
166
+ meta: {
167
+ payload,
168
+ key: primaryKey,
169
+ collection: this.collection,
170
+ },
171
+ context: {
172
+ database: (0, database_1.default)(),
173
+ schema: this.schema,
174
+ accountability: this.accountability,
175
+ },
176
+ };
177
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
178
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
179
+ }
180
+ else {
181
+ opts.bypassEmitAction(actionEvent);
182
+ }
183
+ for (const nestedActionEvent of nestedActionEvents) {
184
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
185
+ }
168
186
  }
169
187
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
170
188
  await this.cache.clear();
@@ -197,7 +215,16 @@ class ItemsService {
197
215
  * Get items by query
198
216
  */
199
217
  async readByQuery(query, opts) {
200
- let ast = await (0, get_ast_from_query_1.default)(this.collection, query, this.schema, {
218
+ const updatedQuery = (opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false
219
+ ? await emitter_1.default.emitFilter(`${this.eventScope}.query`, query, {
220
+ collection: this.collection,
221
+ }, {
222
+ database: this.knex,
223
+ schema: this.schema,
224
+ accountability: this.accountability,
225
+ })
226
+ : query;
227
+ let ast = await (0, get_ast_from_query_1.default)(this.collection, updatedQuery, this.schema, {
201
228
  accountability: this.accountability,
202
229
  // By setting the permissions action, you can read items using the permissions for another
203
230
  // operation's permissions. This is used to dynamically check if you have update/delete
@@ -223,7 +250,7 @@ class ItemsService {
223
250
  }
224
251
  const filteredRecords = (opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false
225
252
  ? await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, records, {
226
- query,
253
+ query: updatedQuery,
227
254
  collection: this.collection,
228
255
  }, {
229
256
  database: this.knex,
@@ -234,7 +261,7 @@ class ItemsService {
234
261
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
235
262
  emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, {
236
263
  payload: filteredRecords,
237
- query,
264
+ query: updatedQuery,
238
265
  collection: this.collection,
239
266
  }, {
240
267
  database: this.knex || (0, database_1.default)(),
@@ -323,6 +350,7 @@ class ItemsService {
323
350
  .filter((field) => field.alias === true)
324
351
  .map((field) => field.field);
325
352
  const payload = (0, lodash_1.cloneDeep)(data);
353
+ const nestedActionEvents = [];
326
354
  const authorizationService = new authorization_1.AuthorizationService({
327
355
  accountability: this.accountability,
328
356
  knex: this.knex,
@@ -356,8 +384,8 @@ class ItemsService {
356
384
  knex: trx,
357
385
  schema: this.schema,
358
386
  });
359
- const { payload: payloadWithM2O, revisions: revisionsM2O } = await payloadService.processM2O(payloadWithPresets);
360
- const { payload: payloadWithA2O, revisions: revisionsA2O } = await payloadService.processA2O(payloadWithM2O);
387
+ const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
388
+ const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
361
389
  const payloadWithoutAliasAndPK = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, primaryKeyField, ...aliases));
362
390
  const payloadWithTypeCasting = await payloadService.processValues('update', payloadWithoutAliasAndPK);
363
391
  if (Object.keys(payloadWithTypeCasting).length > 0) {
@@ -369,9 +397,12 @@ class ItemsService {
369
397
  }
370
398
  }
371
399
  const childrenRevisions = [...revisionsM2O, ...revisionsA2O];
400
+ nestedActionEvents.push(...nestedActionEventsM2O);
401
+ nestedActionEvents.push(...nestedActionEventsA2O);
372
402
  for (const key of keys) {
373
- const { revisions } = await payloadService.processO2M(payload, key);
403
+ const { revisions, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payload, key, opts);
374
404
  childrenRevisions.push(...revisions);
405
+ nestedActionEvents.push(...nestedActionEventsO2M);
375
406
  }
376
407
  // If this is an authenticated action, and accountability tracking is enabled, save activity row
377
408
  if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
@@ -385,6 +416,7 @@ class ItemsService {
385
416
  collection: this.collection,
386
417
  ip: this.accountability.ip,
387
418
  user_agent: this.accountability.userAgent,
419
+ origin: this.accountability.origin,
388
420
  item: key,
389
421
  })));
390
422
  if (this.schema.collections[this.collection].accountability === 'all') {
@@ -427,17 +459,30 @@ class ItemsService {
427
459
  await this.cache.clear();
428
460
  }
429
461
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
430
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.update', `${this.collection}.items.update`] : `${this.eventScope}.update`, {
431
- payload,
432
- keys,
433
- collection: this.collection,
434
- }, {
435
- // This hook is called async. If we would pass the transaction here, the hook can be
436
- // called after the transaction is done #5460
437
- database: this.knex || (0, database_1.default)(),
438
- schema: this.schema,
439
- accountability: this.accountability,
440
- });
462
+ const actionEvent = {
463
+ event: this.eventScope === 'items'
464
+ ? ['items.update', `${this.collection}.items.update`]
465
+ : `${this.eventScope}.update`,
466
+ meta: {
467
+ payload,
468
+ keys,
469
+ collection: this.collection,
470
+ },
471
+ context: {
472
+ database: (0, database_1.default)(),
473
+ schema: this.schema,
474
+ accountability: this.accountability,
475
+ },
476
+ };
477
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
478
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
479
+ }
480
+ else {
481
+ opts.bypassEmitAction(actionEvent);
482
+ }
483
+ for (const nestedActionEvent of nestedActionEvents) {
484
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
485
+ }
441
486
  }
442
487
  return keys;
443
488
  }
@@ -539,6 +584,7 @@ class ItemsService {
539
584
  collection: this.collection,
540
585
  ip: this.accountability.ip,
541
586
  user_agent: this.accountability.userAgent,
587
+ origin: this.accountability.origin,
542
588
  item: key,
543
589
  })));
544
590
  }
@@ -547,17 +593,27 @@ class ItemsService {
547
593
  await this.cache.clear();
548
594
  }
549
595
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
550
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, {
551
- payload: keys,
552
- keys: keys,
553
- collection: this.collection,
554
- }, {
555
- // This hook is called async. If we would pass the transaction here, the hook can be
556
- // called after the transaction is done #5460
557
- database: this.knex || (0, database_1.default)(),
558
- schema: this.schema,
559
- accountability: this.accountability,
560
- });
596
+ const actionEvent = {
597
+ event: this.eventScope === 'items'
598
+ ? ['items.delete', `${this.collection}.items.delete`]
599
+ : `${this.eventScope}.delete`,
600
+ meta: {
601
+ payload: keys,
602
+ keys: keys,
603
+ collection: this.collection,
604
+ },
605
+ context: {
606
+ database: (0, database_1.default)(),
607
+ schema: this.schema,
608
+ accountability: this.accountability,
609
+ },
610
+ };
611
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
612
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
613
+ }
614
+ else {
615
+ opts.bypassEmitAction(actionEvent);
616
+ }
561
617
  }
562
618
  return keys;
563
619
  }
@@ -0,0 +1 @@
1
+ export {};