directus 9.20.4 → 9.21.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 (135) hide show
  1. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  2. package/dist/controllers/extensions.js +4 -13
  3. package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -1
  4. package/dist/database/helpers/date/dialects/sqlite.js +4 -0
  5. package/dist/database/helpers/date/types.d.ts +1 -1
  6. package/dist/database/helpers/date/types.js +4 -0
  7. package/dist/database/helpers/fn/dialects/mssql.d.ts +8 -8
  8. package/dist/database/helpers/fn/dialects/mssql.js +22 -16
  9. package/dist/database/helpers/fn/dialects/mysql.d.ts +8 -8
  10. package/dist/database/helpers/fn/dialects/mysql.js +22 -16
  11. package/dist/database/helpers/fn/dialects/postgres.d.ts +8 -8
  12. package/dist/database/helpers/fn/dialects/postgres.js +22 -16
  13. package/dist/database/helpers/fn/types.d.ts +1 -1
  14. package/dist/database/helpers/index.d.ts +1 -1
  15. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
  16. package/dist/database/helpers/schema/dialects/cockroachdb.js +11 -0
  17. package/dist/database/helpers/schema/types.d.ts +3 -2
  18. package/dist/database/helpers/schema/types.js +5 -0
  19. package/dist/database/migrations/run.js +29 -3
  20. package/dist/database/run-ast.d.ts +1 -1
  21. package/dist/database/run-ast.js +1 -1
  22. package/dist/env.d.ts +4 -0
  23. package/dist/env.js +9 -4
  24. package/dist/env.test.d.ts +1 -8
  25. package/dist/exceptions/database/contains-null-values.d.ts +1 -1
  26. package/dist/exceptions/database/dialects/types.d.ts +6 -6
  27. package/dist/exceptions/database/invalid-foreign-key.d.ts +1 -1
  28. package/dist/exceptions/database/not-null-violation.d.ts +1 -1
  29. package/dist/exceptions/database/record-not-unique.d.ts +1 -1
  30. package/dist/exceptions/database/value-out-of-range.d.ts +1 -1
  31. package/dist/exceptions/database/value-too-long.d.ts +1 -1
  32. package/dist/exceptions/hit-rate-limit.d.ts +1 -1
  33. package/dist/exceptions/method-not-allowed.d.ts +1 -1
  34. package/dist/exceptions/service-unavailable.d.ts +1 -1
  35. package/dist/extensions.d.ts +7 -7
  36. package/dist/extensions.js +92 -89
  37. package/dist/logger.d.ts +1 -0
  38. package/dist/messenger.d.ts +1 -1
  39. package/dist/middleware/authenticate.d.ts +1 -0
  40. package/dist/middleware/validate-batch.d.ts +2 -0
  41. package/dist/operations/condition/index.d.ts +1 -1
  42. package/dist/operations/condition/index.js +1 -1
  43. package/dist/operations/condition/index.test.d.ts +1 -0
  44. package/dist/operations/exec/index.d.ts +1 -1
  45. package/dist/operations/item-create/index.d.ts +1 -1
  46. package/dist/operations/item-delete/index.d.ts +1 -1
  47. package/dist/operations/item-read/index.d.ts +1 -1
  48. package/dist/operations/item-update/index.d.ts +1 -1
  49. package/dist/operations/log/index.d.ts +1 -1
  50. package/dist/operations/mail/index.d.ts +1 -1
  51. package/dist/operations/notification/index.d.ts +1 -1
  52. package/dist/operations/request/index.d.ts +1 -1
  53. package/dist/operations/sleep/index.d.ts +1 -1
  54. package/dist/operations/transform/index.d.ts +1 -1
  55. package/dist/operations/trigger/index.d.ts +1 -1
  56. package/dist/operations/trigger/index.js +5 -2
  57. package/dist/rate-limiter.d.ts +1 -1
  58. package/dist/services/authorization.js +7 -3
  59. package/dist/services/collections.d.ts +1 -1
  60. package/dist/services/collections.js +112 -13
  61. package/dist/services/fields.d.ts +2 -2
  62. package/dist/services/fields.js +89 -41
  63. package/dist/services/fields.test.d.ts +1 -0
  64. package/dist/services/graphql/index.js +4 -1
  65. package/dist/services/graphql/utils/process-error.d.ts +4 -0
  66. package/dist/services/graphql/utils/process-error.js +26 -0
  67. package/dist/services/graphql/utils/process-error.test.d.ts +1 -0
  68. package/dist/services/items.d.ts +1 -1
  69. package/dist/services/items.js +39 -13
  70. package/dist/services/mail/index.d.ts +2 -2
  71. package/dist/services/mail/index.js +2 -1
  72. package/dist/services/mail/templates/base.liquid +4 -4
  73. package/dist/services/notifications.js +9 -4
  74. package/dist/services/notifications.test.d.ts +1 -0
  75. package/dist/services/payload.d.ts +2 -2
  76. package/dist/services/payload.js +14 -12
  77. package/dist/services/relations.d.ts +2 -2
  78. package/dist/services/relations.js +54 -4
  79. package/dist/services/users.js +2 -2
  80. package/dist/services/users.test.d.ts +1 -0
  81. package/dist/types/assets.d.ts +7 -7
  82. package/dist/types/ast.d.ts +7 -7
  83. package/dist/types/auth.d.ts +4 -4
  84. package/dist/types/collection.d.ts +2 -2
  85. package/dist/types/events.d.ts +1 -1
  86. package/dist/types/files.d.ts +2 -2
  87. package/dist/types/items.d.ts +5 -5
  88. package/dist/types/migration.d.ts +1 -1
  89. package/dist/types/revision.d.ts +1 -1
  90. package/dist/types/services.d.ts +1 -1
  91. package/dist/types/snapshot.d.ts +4 -4
  92. package/dist/types/webhooks.d.ts +2 -2
  93. package/dist/utils/get-ast-from-query.d.ts +1 -1
  94. package/dist/utils/get-column-path.d.ts +2 -2
  95. package/dist/utils/get-module-default.d.ts +1 -1
  96. package/dist/utils/get-relation-info.d.ts +1 -1
  97. package/dist/utils/job-queue.d.ts +1 -1
  98. package/dist/utils/merge-permissions.d.ts +1 -0
  99. package/dist/utils/reduce-schema.js +3 -1
  100. package/package.json +70 -71
  101. package/dist/__mocks__/cache.d.ts +0 -5
  102. package/dist/__mocks__/cache.js +0 -7
  103. package/dist/__utils__/items-utils.d.ts +0 -2
  104. package/dist/__utils__/items-utils.js +0 -36
  105. package/dist/__utils__/schemas.d.ts +0 -13
  106. package/dist/__utils__/schemas.js +0 -304
  107. package/dist/__utils__/snapshots.d.ts +0 -5
  108. package/dist/__utils__/snapshots.js +0 -897
  109. package/dist/cli/index.test.js +0 -63
  110. package/dist/controllers/files.test.js +0 -49
  111. package/dist/database/migrations/run.test.js +0 -92
  112. package/dist/env.test.js +0 -40
  113. package/dist/middleware/authenticate.test.js +0 -214
  114. package/dist/middleware/extract-token.test.js +0 -60
  115. package/dist/middleware/validate-batch.test.js +0 -82
  116. package/dist/operations/exec/index.test.js +0 -95
  117. package/dist/services/files.test.js +0 -89
  118. package/dist/services/items.test.js +0 -765
  119. package/dist/services/payload.test.js +0 -196
  120. package/dist/services/specifications.test.js +0 -96
  121. package/dist/utils/apply-snapshot.test.js +0 -305
  122. package/dist/utils/async-handler.test.js +0 -18
  123. package/dist/utils/calculate-field-depth.test.js +0 -76
  124. package/dist/utils/filter-items.test.js +0 -60
  125. package/dist/utils/get-auth-providers.test.js +0 -72
  126. package/dist/utils/get-cache-key.test.js +0 -74
  127. package/dist/utils/get-column-path.test.js +0 -136
  128. package/dist/utils/get-config-from-env.test.js +0 -19
  129. package/dist/utils/get-relation-info.test.js +0 -88
  130. package/dist/utils/get-relation-type.test.js +0 -69
  131. package/dist/utils/get-string-byte-size.test.js +0 -8
  132. package/dist/utils/is-directus-jwt.test.js +0 -26
  133. package/dist/utils/jwt.test.js +0 -36
  134. package/dist/utils/merge-permissions.test.js +0 -80
  135. package/dist/utils/validate-keys.test.js +0 -97
@@ -39,6 +39,8 @@ const items_1 = require("../services/items");
39
39
  const utils_1 = require("@directus/shared/utils");
40
40
  const helpers_1 = require("../database/helpers");
41
41
  const lodash_1 = require("lodash");
42
+ const get_schema_1 = require("../utils/get-schema");
43
+ const emitter_1 = __importDefault(require("../emitter"));
42
44
  class CollectionsService {
43
45
  constructor(options) {
44
46
  this.knex = options.knex || (0, database_1.default)();
@@ -63,6 +65,7 @@ class CollectionsService {
63
65
  if (payload.collection.startsWith('directus_')) {
64
66
  throw new exceptions_1.InvalidPayloadException(`Collections can't start with "directus_"`);
65
67
  }
68
+ const nestedActionEvents = [];
66
69
  try {
67
70
  const existingCollections = [
68
71
  ...((_b = (_a = (await this.knex.select('collection').from('directus_collections'))) === null || _a === void 0 ? void 0 : _a.map(({ collection }) => collection)) !== null && _b !== void 0 ? _b : []),
@@ -125,7 +128,9 @@ class CollectionsService {
125
128
  schema: this.schema,
126
129
  });
127
130
  const fieldPayloads = payload.fields.filter((field) => field.meta).map((field) => field.meta);
128
- await fieldItemsService.createMany(fieldPayloads);
131
+ await fieldItemsService.createMany(fieldPayloads, {
132
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
133
+ });
129
134
  }
130
135
  if (payload.meta) {
131
136
  const collectionItemsService = new items_1.ItemsService('directus_collections', {
@@ -136,6 +141,8 @@ class CollectionsService {
136
141
  await collectionItemsService.createOne({
137
142
  ...payload.meta,
138
143
  collection: payload.collection,
144
+ }, {
145
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
139
146
  });
140
147
  }
141
148
  return payload.collection;
@@ -149,12 +156,20 @@ class CollectionsService {
149
156
  if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
150
157
  await (0, cache_1.clearSystemCache)();
151
158
  }
159
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
160
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
161
+ for (const nestedActionEvent of nestedActionEvents) {
162
+ nestedActionEvent.context.schema = updatedSchema;
163
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
164
+ }
165
+ }
152
166
  }
153
167
  }
154
168
  /**
155
169
  * Create multiple new collections
156
170
  */
157
171
  async createMany(payloads, opts) {
172
+ const nestedActionEvents = [];
158
173
  try {
159
174
  const collections = await this.knex.transaction(async (trx) => {
160
175
  const service = new CollectionsService({
@@ -164,7 +179,11 @@ class CollectionsService {
164
179
  });
165
180
  const collectionNames = [];
166
181
  for (const payload of payloads) {
167
- const name = await service.createOne(payload, { autoPurgeCache: false, autoPurgeSystemCache: false });
182
+ const name = await service.createOne(payload, {
183
+ autoPurgeCache: false,
184
+ autoPurgeSystemCache: false,
185
+ bypassEmitAction: (params) => nestedActionEvents.push(params),
186
+ });
168
187
  collectionNames.push(name);
169
188
  }
170
189
  return collectionNames;
@@ -175,7 +194,16 @@ class CollectionsService {
175
194
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
176
195
  await this.cache.clear();
177
196
  }
178
- await (0, cache_1.clearSystemCache)();
197
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
198
+ await (0, cache_1.clearSystemCache)();
199
+ }
200
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
201
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
202
+ for (const nestedActionEvent of nestedActionEvents) {
203
+ nestedActionEvent.context.schema = updatedSchema;
204
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
205
+ }
206
+ }
179
207
  }
180
208
  }
181
209
  /**
@@ -277,6 +305,7 @@ class CollectionsService {
277
305
  if (this.accountability && this.accountability.admin !== true) {
278
306
  throw new exceptions_1.ForbiddenException();
279
307
  }
308
+ const nestedActionEvents = [];
280
309
  try {
281
310
  const collectionItemsService = new items_1.ItemsService('directus_collections', {
282
311
  knex: this.knex,
@@ -293,10 +322,16 @@ class CollectionsService {
293
322
  .where({ collection: collectionKey })
294
323
  .first());
295
324
  if (exists) {
296
- await collectionItemsService.updateOne(collectionKey, payload.meta, opts);
325
+ await collectionItemsService.updateOne(collectionKey, payload.meta, {
326
+ ...opts,
327
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
328
+ });
297
329
  }
298
330
  else {
299
- await collectionItemsService.createOne({ ...payload.meta, collection: collectionKey }, opts);
331
+ await collectionItemsService.createOne({ ...payload.meta, collection: collectionKey }, {
332
+ ...opts,
333
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
334
+ });
300
335
  }
301
336
  return collectionKey;
302
337
  }
@@ -307,6 +342,13 @@ class CollectionsService {
307
342
  if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
308
343
  await (0, cache_1.clearSystemCache)();
309
344
  }
345
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
346
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
347
+ for (const nestedActionEvent of nestedActionEvents) {
348
+ nestedActionEvent.context.schema = updatedSchema;
349
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
350
+ }
351
+ }
310
352
  }
311
353
  }
312
354
  /**
@@ -321,6 +363,7 @@ class CollectionsService {
321
363
  }
322
364
  const collectionKey = 'collection';
323
365
  const collectionKeys = [];
366
+ const nestedActionEvents = [];
324
367
  try {
325
368
  await this.knex.transaction(async (trx) => {
326
369
  const collectionItemsService = new CollectionsService({
@@ -334,6 +377,7 @@ class CollectionsService {
334
377
  await collectionItemsService.updateOne(payload[collectionKey], (0, lodash_1.omit)(payload, collectionKey), {
335
378
  autoPurgeCache: false,
336
379
  autoPurgeSystemCache: false,
380
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
337
381
  });
338
382
  collectionKeys.push(payload[collectionKey]);
339
383
  }
@@ -343,7 +387,16 @@ class CollectionsService {
343
387
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
344
388
  await this.cache.clear();
345
389
  }
346
- await (0, cache_1.clearSystemCache)();
390
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
391
+ await (0, cache_1.clearSystemCache)();
392
+ }
393
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
394
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
395
+ for (const nestedActionEvent of nestedActionEvents) {
396
+ nestedActionEvent.context.schema = updatedSchema;
397
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
398
+ }
399
+ }
347
400
  }
348
401
  return collectionKeys;
349
402
  }
@@ -354,6 +407,7 @@ class CollectionsService {
354
407
  if (this.accountability && this.accountability.admin !== true) {
355
408
  throw new exceptions_1.ForbiddenException();
356
409
  }
410
+ const nestedActionEvents = [];
357
411
  try {
358
412
  await this.knex.transaction(async (trx) => {
359
413
  const service = new CollectionsService({
@@ -362,7 +416,11 @@ class CollectionsService {
362
416
  knex: trx,
363
417
  });
364
418
  for (const collectionKey of collectionKeys) {
365
- await service.updateOne(collectionKey, data, { autoPurgeCache: false, autoPurgeSystemCache: false });
419
+ await service.updateOne(collectionKey, data, {
420
+ autoPurgeCache: false,
421
+ autoPurgeSystemCache: false,
422
+ bypassEmitAction: (params) => nestedActionEvents.push(params),
423
+ });
366
424
  }
367
425
  });
368
426
  return collectionKeys;
@@ -371,7 +429,16 @@ class CollectionsService {
371
429
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
372
430
  await this.cache.clear();
373
431
  }
374
- await (0, cache_1.clearSystemCache)();
432
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
433
+ await (0, cache_1.clearSystemCache)();
434
+ }
435
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
436
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
437
+ for (const nestedActionEvent of nestedActionEvents) {
438
+ nestedActionEvent.context.schema = updatedSchema;
439
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
440
+ }
441
+ }
375
442
  }
376
443
  }
377
444
  /**
@@ -382,6 +449,7 @@ class CollectionsService {
382
449
  if (this.accountability && this.accountability.admin !== true) {
383
450
  throw new exceptions_1.ForbiddenException();
384
451
  }
452
+ const nestedActionEvents = [];
385
453
  try {
386
454
  const collections = await this.readByQuery();
387
455
  const collectionToBeDeleted = collections.find((collection) => collection.collection === collectionKey);
@@ -401,7 +469,9 @@ class CollectionsService {
401
469
  accountability: this.accountability,
402
470
  schema: this.schema,
403
471
  });
404
- await collectionItemsService.deleteOne(collectionKey);
472
+ await collectionItemsService.deleteOne(collectionKey, {
473
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
474
+ });
405
475
  }
406
476
  if (collectionToBeDeleted.schema) {
407
477
  const fieldsService = new fields_1.FieldsService({
@@ -429,11 +499,19 @@ class CollectionsService {
429
499
  for (const relation of relations) {
430
500
  // Delete related o2m fields that point to current collection
431
501
  if (relation.related_collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field)) {
432
- await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
502
+ await fieldsService.deleteField(relation.related_collection, relation.meta.one_field, {
503
+ autoPurgeCache: false,
504
+ autoPurgeSystemCache: false,
505
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
506
+ });
433
507
  }
434
508
  // Delete related m2o fields that point to current collection
435
509
  if (relation.related_collection === collectionKey) {
436
- await fieldsService.deleteField(relation.collection, relation.field);
510
+ await fieldsService.deleteField(relation.collection, relation.field, {
511
+ autoPurgeCache: false,
512
+ autoPurgeSystemCache: false,
513
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
514
+ });
437
515
  }
438
516
  }
439
517
  const a2oRelationsThatIncludeThisCollection = this.schema.relations.filter((relation) => {
@@ -459,6 +537,13 @@ class CollectionsService {
459
537
  if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
460
538
  await (0, cache_1.clearSystemCache)();
461
539
  }
540
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
541
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
542
+ for (const nestedActionEvent of nestedActionEvents) {
543
+ nestedActionEvent.context.schema = updatedSchema;
544
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
545
+ }
546
+ }
462
547
  }
463
548
  }
464
549
  /**
@@ -468,6 +553,7 @@ class CollectionsService {
468
553
  if (this.accountability && this.accountability.admin !== true) {
469
554
  throw new exceptions_1.ForbiddenException();
470
555
  }
556
+ const nestedActionEvents = [];
471
557
  try {
472
558
  await this.knex.transaction(async (trx) => {
473
559
  const service = new CollectionsService({
@@ -476,7 +562,11 @@ class CollectionsService {
476
562
  knex: trx,
477
563
  });
478
564
  for (const collectionKey of collectionKeys) {
479
- await service.deleteOne(collectionKey, { autoPurgeCache: false, autoPurgeSystemCache: false });
565
+ await service.deleteOne(collectionKey, {
566
+ autoPurgeCache: false,
567
+ autoPurgeSystemCache: false,
568
+ bypassEmitAction: (params) => nestedActionEvents.push(params),
569
+ });
480
570
  }
481
571
  });
482
572
  return collectionKeys;
@@ -485,7 +575,16 @@ class CollectionsService {
485
575
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
486
576
  await this.cache.clear();
487
577
  }
488
- await (0, cache_1.clearSystemCache)();
578
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
579
+ await (0, cache_1.clearSystemCache)();
580
+ }
581
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
582
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
583
+ for (const nestedActionEvent of nestedActionEvents) {
584
+ nestedActionEvent.context.schema = updatedSchema;
585
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
586
+ }
587
+ }
489
588
  }
490
589
  }
491
590
  }
@@ -6,7 +6,7 @@ import { Column } from 'knex-schema-inspector/dist/types/column';
6
6
  import { Helpers } from '../database/helpers';
7
7
  import { ItemsService } from '../services/items';
8
8
  import { PayloadService } from '../services/payload';
9
- import { AbstractServiceOptions } from '../types';
9
+ import { AbstractServiceOptions, MutationOptions } from '../types';
10
10
  export declare class FieldsService {
11
11
  knex: Knex;
12
12
  helpers: Helpers;
@@ -26,6 +26,6 @@ export declare class FieldsService {
26
26
  type: Type | null;
27
27
  }, table?: Knex.CreateTableBuilder): Promise<void>;
28
28
  updateField(collection: string, field: RawField): Promise<string>;
29
- deleteField(collection: string, field: string): Promise<void>;
29
+ deleteField(collection: string, field: string, opts?: MutationOptions): Promise<void>;
30
30
  addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, alter?: Column | null): void;
31
31
  }
@@ -46,6 +46,7 @@ const get_default_value_1 = __importDefault(require("../utils/get-default-value"
46
46
  const get_local_type_1 = __importDefault(require("../utils/get-local-type"));
47
47
  const relations_1 = require("./relations");
48
48
  const constants_3 = require("@directus/shared/constants");
49
+ const get_schema_1 = require("../utils/get-schema");
49
50
  class FieldsService {
50
51
  constructor(options) {
51
52
  this.knex = options.knex || (0, database_1.default)();
@@ -223,6 +224,7 @@ class FieldsService {
223
224
  throw new exceptions_1.ForbiddenException();
224
225
  }
225
226
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
227
+ const nestedActionEvents = [];
226
228
  try {
227
229
  const exists = field.field in this.schema.collections[collection].fields ||
228
230
  (0, lodash_1.isNil)(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
@@ -265,14 +267,18 @@ class FieldsService {
265
267
  field: hookAdjustedField.field,
266
268
  }, { emitEvents: false });
267
269
  }
268
- emitter_1.default.emitAction(`fields.create`, {
269
- payload: hookAdjustedField,
270
- key: hookAdjustedField.field,
271
- collection: collection,
272
- }, {
273
- database: (0, database_1.default)(),
274
- schema: this.schema,
275
- accountability: this.accountability,
270
+ nestedActionEvents.push({
271
+ event: 'fields.create',
272
+ meta: {
273
+ payload: hookAdjustedField,
274
+ key: hookAdjustedField.field,
275
+ collection: collection,
276
+ },
277
+ context: {
278
+ database: (0, database_1.default)(),
279
+ schema: this.schema,
280
+ accountability: this.accountability,
281
+ },
276
282
  });
277
283
  });
278
284
  }
@@ -284,14 +290,20 @@ class FieldsService {
284
290
  await this.cache.clear();
285
291
  }
286
292
  await (0, cache_1.clearSystemCache)();
293
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
294
+ for (const nestedActionEvent of nestedActionEvents) {
295
+ nestedActionEvent.context.schema = updatedSchema;
296
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
297
+ }
287
298
  }
288
299
  }
289
300
  async updateField(collection, field) {
290
- var _a, _b;
301
+ var _a, _b, _c;
291
302
  if (this.accountability && this.accountability.admin !== true) {
292
303
  throw new exceptions_1.ForbiddenException();
293
304
  }
294
305
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
306
+ const nestedActionEvents = [];
295
307
  try {
296
308
  const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
297
309
  keys: [field.field],
@@ -304,10 +316,10 @@ class FieldsService {
304
316
  const record = field.meta
305
317
  ? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
306
318
  : null;
307
- if ((hookAdjustedField.type === 'alias' ||
308
- ((_a = this.schema.collections[collection].fields[field.field]) === null || _a === void 0 ? void 0 : _a.type) === 'alias') &&
309
- hookAdjustedField.type &&
310
- hookAdjustedField.type !== ((_b = this.schema.collections[collection].fields[field.field]) === null || _b === void 0 ? void 0 : _b.type)) {
319
+ if (hookAdjustedField.type &&
320
+ (hookAdjustedField.type === 'alias' ||
321
+ ((_a = this.schema.collections[collection].fields[field.field]) === null || _a === void 0 ? void 0 : _a.type) === 'alias') &&
322
+ hookAdjustedField.type !== ((_c = (_b = this.schema.collections[collection].fields[field.field]) === null || _b === void 0 ? void 0 : _b.type) !== null && _c !== void 0 ? _c : 'alias')) {
311
323
  throw new exceptions_1.InvalidPayloadException('Alias type cannot be changed');
312
324
  }
313
325
  if (hookAdjustedField.schema) {
@@ -341,14 +353,18 @@ class FieldsService {
341
353
  }, { emitEvents: false });
342
354
  }
343
355
  }
344
- emitter_1.default.emitAction(`fields.update`, {
345
- payload: hookAdjustedField,
346
- keys: [hookAdjustedField.field],
347
- collection: collection,
348
- }, {
349
- database: (0, database_1.default)(),
350
- schema: this.schema,
351
- accountability: this.accountability,
356
+ nestedActionEvents.push({
357
+ event: 'fields.update',
358
+ meta: {
359
+ payload: hookAdjustedField,
360
+ keys: [hookAdjustedField.field],
361
+ collection: collection,
362
+ },
363
+ context: {
364
+ database: (0, database_1.default)(),
365
+ schema: this.schema,
366
+ accountability: this.accountability,
367
+ },
352
368
  });
353
369
  return field.field;
354
370
  }
@@ -360,13 +376,19 @@ class FieldsService {
360
376
  await this.cache.clear();
361
377
  }
362
378
  await (0, cache_1.clearSystemCache)();
379
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
380
+ for (const nestedActionEvent of nestedActionEvents) {
381
+ nestedActionEvent.context.schema = updatedSchema;
382
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
383
+ }
363
384
  }
364
385
  }
365
- async deleteField(collection, field) {
386
+ async deleteField(collection, field, opts) {
366
387
  if (this.accountability && this.accountability.admin !== true) {
367
388
  throw new exceptions_1.ForbiddenException();
368
389
  }
369
390
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
391
+ const nestedActionEvents = [];
370
392
  try {
371
393
  await emitter_1.default.emitFilter('fields.delete', [field], {
372
394
  collection: collection,
@@ -396,12 +418,19 @@ class FieldsService {
396
418
  const isM2O = relation.collection === collection && relation.field === field;
397
419
  // If the current field is a m2o, delete the related o2m if it exists and remove the relationship
398
420
  if (isM2O) {
399
- await relationsService.deleteOne(collection, field);
421
+ await relationsService.deleteOne(collection, field, {
422
+ autoPurgeSystemCache: false,
423
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
424
+ });
400
425
  if (relation.related_collection &&
401
426
  ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) &&
402
427
  relation.related_collection !== collection &&
403
428
  relation.meta.one_field !== field) {
404
- await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
429
+ await fieldsService.deleteField(relation.related_collection, relation.meta.one_field, {
430
+ autoPurgeCache: false,
431
+ autoPurgeSystemCache: false,
432
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
433
+ });
405
434
  }
406
435
  }
407
436
  // If the current field is a o2m, just delete the one field config from the relation
@@ -448,27 +477,46 @@ class FieldsService {
448
477
  }
449
478
  await trx('directus_fields').delete().where({ collection, field });
450
479
  });
451
- emitter_1.default.emitAction('fields.delete', {
452
- payload: [field],
453
- collection: collection,
454
- }, {
455
- database: this.knex,
456
- schema: this.schema,
457
- accountability: this.accountability,
458
- });
480
+ const actionEvent = {
481
+ event: 'fields.delete',
482
+ meta: {
483
+ payload: [field],
484
+ collection: collection,
485
+ },
486
+ context: {
487
+ database: this.knex,
488
+ schema: this.schema,
489
+ accountability: this.accountability,
490
+ },
491
+ };
492
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
493
+ opts.bypassEmitAction(actionEvent);
494
+ }
495
+ else {
496
+ nestedActionEvents.push(actionEvent);
497
+ }
459
498
  }
460
499
  finally {
461
500
  if (runPostColumnChange) {
462
501
  await this.helpers.schema.postColumnChange();
463
502
  }
464
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
503
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
465
504
  await this.cache.clear();
466
505
  }
467
- await (0, cache_1.clearSystemCache)();
506
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
507
+ await (0, cache_1.clearSystemCache)();
508
+ }
509
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
510
+ const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
511
+ for (const nestedActionEvent of nestedActionEvents) {
512
+ nestedActionEvent.context.schema = updatedSchema;
513
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
514
+ }
515
+ }
468
516
  }
469
517
  }
470
518
  addColumnToTable(table, field, alter = null) {
471
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
519
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
472
520
  let column;
473
521
  // Don't attempt to add a DB column for alias / corrupt fields
474
522
  if (field.type === 'alias' || field.type === 'unknown')
@@ -487,7 +535,7 @@ class FieldsService {
487
535
  }
488
536
  else if (['float', 'decimal'].includes(field.type)) {
489
537
  const type = field.type;
490
- column = table[type](field.field, ((_d = field.schema) === null || _d === void 0 ? void 0 : _d.numeric_precision) || 10, ((_e = field.schema) === null || _e === void 0 ? void 0 : _e.numeric_scale) || 5);
538
+ column = table[type](field.field, (_e = (_d = field.schema) === null || _d === void 0 ? void 0 : _d.numeric_precision) !== null && _e !== void 0 ? _e : 10, (_g = (_f = field.schema) === null || _f === void 0 ? void 0 : _f.numeric_scale) !== null && _g !== void 0 ? _g : 5);
491
539
  }
492
540
  else if (field.type === 'csv') {
493
541
  column = table.string(field.field);
@@ -510,7 +558,7 @@ class FieldsService {
510
558
  else {
511
559
  throw new exceptions_1.InvalidPayloadException(`Illegal type passed: "${field.type}"`);
512
560
  }
513
- if (((_f = field.schema) === null || _f === void 0 ? void 0 : _f.default_value) !== undefined) {
561
+ if (((_h = field.schema) === null || _h === void 0 ? void 0 : _h.default_value) !== undefined) {
514
562
  if (typeof field.schema.default_value === 'string' &&
515
563
  (field.schema.default_value.toLowerCase() === 'now()' || field.schema.default_value === 'CURRENT_TIMESTAMP')) {
516
564
  column.defaultTo(this.knex.fn.now());
@@ -525,7 +573,7 @@ class FieldsService {
525
573
  column.defaultTo(field.schema.default_value);
526
574
  }
527
575
  }
528
- if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) === false) {
576
+ if (((_j = field.schema) === null || _j === void 0 ? void 0 : _j.is_nullable) === false) {
529
577
  if (!alter || alter.is_nullable === true) {
530
578
  column.notNullable();
531
579
  }
@@ -535,15 +583,15 @@ class FieldsService {
535
583
  column.nullable();
536
584
  }
537
585
  }
538
- if ((_h = field.schema) === null || _h === void 0 ? void 0 : _h.is_primary_key) {
586
+ if ((_k = field.schema) === null || _k === void 0 ? void 0 : _k.is_primary_key) {
539
587
  column.primary().notNullable();
540
588
  }
541
- else if (((_j = field.schema) === null || _j === void 0 ? void 0 : _j.is_unique) === true) {
589
+ else if (((_l = field.schema) === null || _l === void 0 ? void 0 : _l.is_unique) === true) {
542
590
  if (!alter || alter.is_unique === false) {
543
591
  column.unique();
544
592
  }
545
593
  }
546
- else if (((_k = field.schema) === null || _k === void 0 ? void 0 : _k.is_unique) === false) {
594
+ else if (((_m = field.schema) === null || _m === void 0 ? void 0 : _m.is_unique) === false) {
547
595
  if (alter && alter.is_unique === true) {
548
596
  table.dropUnique([field.field]);
549
597
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -8,6 +8,7 @@ const types_1 = require("@directus/shared/types");
8
8
  const argon2_1 = __importDefault(require("argon2"));
9
9
  const graphql_1 = require("graphql");
10
10
  const graphql_compose_1 = require("graphql-compose");
11
+ const process_error_1 = __importDefault(require("./utils/process-error"));
11
12
  const lodash_1 = require("lodash");
12
13
  const ms_1 = __importDefault(require("ms"));
13
14
  const cache_1 = require("../../cache");
@@ -99,7 +100,7 @@ class GraphQLService {
99
100
  }
100
101
  const formattedResult = {
101
102
  ...result,
102
- errors: (_a = result.errors) === null || _a === void 0 ? void 0 : _a.map(graphql_1.formatError),
103
+ errors: (_a = result.errors) === null || _a === void 0 ? void 0 : _a.map((error) => (0, process_error_1.default)(this.accountability, error)),
103
104
  };
104
105
  return formattedResult;
105
106
  }
@@ -1322,6 +1323,8 @@ class GraphQLService {
1322
1323
  for (const subSelection of selection.selectionSet.selections) {
1323
1324
  if (subSelection.kind !== 'Field')
1324
1325
  continue;
1326
+ if (subSelection.name.value.startsWith('__'))
1327
+ continue;
1325
1328
  children.push(`${subSelection.name.value}(${rootField})`);
1326
1329
  }
1327
1330
  }
@@ -0,0 +1,4 @@
1
+ import { GraphQLError, GraphQLFormattedError } from 'graphql';
2
+ import { Accountability } from '@directus/shared/types';
3
+ declare const processError: (accountability: Accountability | null, error: Readonly<GraphQLError>) => GraphQLFormattedError;
4
+ export default processError;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const logger_1 = __importDefault(require("../../../logger"));
7
+ const processError = (accountability, error) => {
8
+ logger_1.default.error(error);
9
+ if ((accountability === null || accountability === void 0 ? void 0 : accountability.admin) === true) {
10
+ return {
11
+ ...error,
12
+ extensions: {
13
+ code: 'INTERNAL_SERVER_ERROR',
14
+ },
15
+ };
16
+ }
17
+ else {
18
+ return {
19
+ message: 'An unexpected error occurred.',
20
+ extensions: {
21
+ code: 'INTERNAL_SERVER_ERROR',
22
+ },
23
+ };
24
+ }
25
+ };
26
+ exports.default = processError;
@@ -2,7 +2,7 @@ import { Accountability, PermissionsAction, Query, SchemaOverview } from '@direc
2
2
  import Keyv from 'keyv';
3
3
  import { Knex } from 'knex';
4
4
  import { AbstractService, AbstractServiceOptions, Item as AnyItem, MutationOptions, PrimaryKey } from '../types';
5
- export declare type QueryOptions = {
5
+ export type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
8
8
  emitEvents?: boolean;