directus 9.20.4 → 9.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/auth/drivers/openid.js +3 -1
  2. package/dist/cli/commands/schema/apply.js +0 -2
  3. package/dist/cli/commands/schema/snapshot.js +0 -2
  4. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  5. package/dist/controllers/extensions.js +4 -13
  6. package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -1
  7. package/dist/database/helpers/date/dialects/sqlite.js +4 -0
  8. package/dist/database/helpers/date/types.d.ts +1 -1
  9. package/dist/database/helpers/date/types.js +4 -0
  10. package/dist/database/helpers/fn/dialects/mssql.d.ts +8 -8
  11. package/dist/database/helpers/fn/dialects/mssql.js +22 -16
  12. package/dist/database/helpers/fn/dialects/mysql.d.ts +8 -8
  13. package/dist/database/helpers/fn/dialects/mysql.js +22 -16
  14. package/dist/database/helpers/fn/dialects/postgres.d.ts +8 -8
  15. package/dist/database/helpers/fn/dialects/postgres.js +22 -16
  16. package/dist/database/helpers/fn/types.d.ts +1 -1
  17. package/dist/database/helpers/index.d.ts +1 -1
  18. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
  19. package/dist/database/helpers/schema/dialects/cockroachdb.js +11 -0
  20. package/dist/database/helpers/schema/types.d.ts +3 -2
  21. package/dist/database/helpers/schema/types.js +5 -0
  22. package/dist/database/migrations/run.js +29 -3
  23. package/dist/database/run-ast.d.ts +1 -1
  24. package/dist/database/run-ast.js +1 -1
  25. package/dist/env.d.ts +4 -0
  26. package/dist/env.js +9 -4
  27. package/dist/env.test.d.ts +1 -8
  28. package/dist/exceptions/database/contains-null-values.d.ts +1 -1
  29. package/dist/exceptions/database/dialects/types.d.ts +6 -6
  30. package/dist/exceptions/database/invalid-foreign-key.d.ts +1 -1
  31. package/dist/exceptions/database/not-null-violation.d.ts +1 -1
  32. package/dist/exceptions/database/record-not-unique.d.ts +1 -1
  33. package/dist/exceptions/database/value-out-of-range.d.ts +1 -1
  34. package/dist/exceptions/database/value-too-long.d.ts +1 -1
  35. package/dist/exceptions/hit-rate-limit.d.ts +1 -1
  36. package/dist/exceptions/method-not-allowed.d.ts +1 -1
  37. package/dist/exceptions/service-unavailable.d.ts +1 -1
  38. package/dist/extensions.d.ts +7 -7
  39. package/dist/extensions.js +92 -89
  40. package/dist/logger.d.ts +1 -0
  41. package/dist/messenger.d.ts +1 -1
  42. package/dist/middleware/authenticate.d.ts +1 -0
  43. package/dist/middleware/schema.js +1 -1
  44. package/dist/middleware/validate-batch.d.ts +2 -0
  45. package/dist/operations/condition/index.d.ts +1 -1
  46. package/dist/operations/condition/index.js +1 -1
  47. package/dist/operations/condition/index.test.d.ts +1 -0
  48. package/dist/operations/exec/index.d.ts +1 -1
  49. package/dist/operations/item-create/index.d.ts +1 -1
  50. package/dist/operations/item-delete/index.d.ts +1 -1
  51. package/dist/operations/item-read/index.d.ts +1 -1
  52. package/dist/operations/item-update/index.d.ts +1 -1
  53. package/dist/operations/log/index.d.ts +1 -1
  54. package/dist/operations/mail/index.d.ts +1 -1
  55. package/dist/operations/notification/index.d.ts +1 -1
  56. package/dist/operations/request/index.d.ts +1 -1
  57. package/dist/operations/sleep/index.d.ts +1 -1
  58. package/dist/operations/transform/index.d.ts +1 -1
  59. package/dist/operations/trigger/index.d.ts +1 -1
  60. package/dist/operations/trigger/index.js +5 -2
  61. package/dist/rate-limiter.d.ts +1 -1
  62. package/dist/services/authorization.js +7 -3
  63. package/dist/services/collections.d.ts +1 -1
  64. package/dist/services/collections.js +112 -13
  65. package/dist/services/fields.d.ts +5 -4
  66. package/dist/services/fields.js +118 -50
  67. package/dist/services/fields.test.d.ts +1 -0
  68. package/dist/services/graphql/index.js +4 -1
  69. package/dist/services/graphql/utils/process-error.d.ts +4 -0
  70. package/dist/services/graphql/utils/process-error.js +26 -0
  71. package/dist/services/graphql/utils/process-error.test.d.ts +1 -0
  72. package/dist/services/items.d.ts +1 -1
  73. package/dist/services/items.js +39 -13
  74. package/dist/services/mail/index.d.ts +2 -2
  75. package/dist/services/mail/index.js +2 -1
  76. package/dist/services/mail/templates/base.liquid +4 -4
  77. package/dist/services/notifications.js +9 -4
  78. package/dist/services/notifications.test.d.ts +1 -0
  79. package/dist/services/payload.d.ts +2 -2
  80. package/dist/services/payload.js +14 -12
  81. package/dist/services/relations.d.ts +4 -4
  82. package/dist/services/relations.js +66 -8
  83. package/dist/services/users.js +8 -2
  84. package/dist/services/users.test.d.ts +1 -0
  85. package/dist/types/assets.d.ts +7 -7
  86. package/dist/types/ast.d.ts +7 -7
  87. package/dist/types/auth.d.ts +4 -4
  88. package/dist/types/collection.d.ts +2 -2
  89. package/dist/types/events.d.ts +1 -1
  90. package/dist/types/files.d.ts +2 -2
  91. package/dist/types/items.d.ts +5 -5
  92. package/dist/types/migration.d.ts +1 -1
  93. package/dist/types/revision.d.ts +1 -1
  94. package/dist/types/services.d.ts +1 -1
  95. package/dist/types/snapshot.d.ts +4 -4
  96. package/dist/types/webhooks.d.ts +2 -2
  97. package/dist/utils/apply-snapshot.js +32 -13
  98. package/dist/utils/get-ast-from-query.d.ts +1 -1
  99. package/dist/utils/get-column-path.d.ts +2 -2
  100. package/dist/utils/get-module-default.d.ts +1 -1
  101. package/dist/utils/get-relation-info.d.ts +1 -1
  102. package/dist/utils/get-schema.d.ts +6 -2
  103. package/dist/utils/get-schema.js +1 -1
  104. package/dist/utils/get-snapshot.js +1 -1
  105. package/dist/utils/job-queue.d.ts +1 -1
  106. package/dist/utils/merge-permissions.d.ts +1 -0
  107. package/dist/utils/reduce-schema.js +3 -1
  108. package/package.json +69 -80
  109. package/dist/__mocks__/cache.d.ts +0 -5
  110. package/dist/__mocks__/cache.js +0 -7
  111. package/dist/__utils__/items-utils.d.ts +0 -2
  112. package/dist/__utils__/items-utils.js +0 -36
  113. package/dist/__utils__/schemas.d.ts +0 -13
  114. package/dist/__utils__/schemas.js +0 -304
  115. package/dist/__utils__/snapshots.d.ts +0 -5
  116. package/dist/__utils__/snapshots.js +0 -897
  117. package/dist/cli/index.test.js +0 -63
  118. package/dist/controllers/files.test.js +0 -49
  119. package/dist/database/migrations/run.test.js +0 -92
  120. package/dist/env.test.js +0 -40
  121. package/dist/middleware/authenticate.test.js +0 -214
  122. package/dist/middleware/extract-token.test.js +0 -60
  123. package/dist/middleware/validate-batch.test.js +0 -82
  124. package/dist/operations/exec/index.test.js +0 -95
  125. package/dist/services/files.test.js +0 -89
  126. package/dist/services/items.test.js +0 -765
  127. package/dist/services/payload.test.js +0 -196
  128. package/dist/services/specifications.test.js +0 -96
  129. package/dist/utils/apply-snapshot.test.js +0 -305
  130. package/dist/utils/async-handler.test.js +0 -18
  131. package/dist/utils/calculate-field-depth.test.js +0 -76
  132. package/dist/utils/filter-items.test.js +0 -60
  133. package/dist/utils/get-auth-providers.test.js +0 -72
  134. package/dist/utils/get-cache-key.test.js +0 -74
  135. package/dist/utils/get-column-path.test.js +0 -136
  136. package/dist/utils/get-config-from-env.test.js +0 -19
  137. package/dist/utils/get-relation-info.test.js +0 -88
  138. package/dist/utils/get-relation-type.test.js +0 -69
  139. package/dist/utils/get-string-byte-size.test.js +0 -8
  140. package/dist/utils/is-directus-jwt.test.js +0 -26
  141. package/dist/utils/jwt.test.js +0 -36
  142. package/dist/utils/merge-permissions.test.js +0 -80
  143. 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)();
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)();
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)();
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)();
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)();
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)();
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)();
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;
@@ -24,8 +24,9 @@ export declare class FieldsService {
24
24
  createField(collection: string, field: Partial<Field> & {
25
25
  field: string;
26
26
  type: Type | null;
27
- }, table?: Knex.CreateTableBuilder): Promise<void>;
28
- updateField(collection: string, field: RawField): Promise<string>;
29
- deleteField(collection: string, field: string): Promise<void>;
27
+ }, table?: Knex.CreateTableBuilder, // allows collection creation to
28
+ opts?: MutationOptions): Promise<void>;
29
+ updateField(collection: string, field: RawField, opts?: MutationOptions): Promise<string>;
30
+ deleteField(collection: string, field: string, opts?: MutationOptions): Promise<void>;
30
31
  addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, alter?: Column | null): void;
31
32
  }
@@ -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)();
@@ -217,12 +218,13 @@ class FieldsService {
217
218
  };
218
219
  return data;
219
220
  }
220
- async createField(collection, field, table // allows collection creation to
221
- ) {
221
+ async createField(collection, field, table, // allows collection creation to
222
+ opts) {
222
223
  if (this.accountability && this.accountability.admin !== true) {
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,33 +267,53 @@ 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,
276
- });
270
+ const actionEvent = {
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
+ },
282
+ };
283
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
284
+ opts.bypassEmitAction(actionEvent);
285
+ }
286
+ else {
287
+ nestedActionEvents.push(actionEvent);
288
+ }
277
289
  });
278
290
  }
279
291
  finally {
280
292
  if (runPostColumnChange) {
281
293
  await this.helpers.schema.postColumnChange();
282
294
  }
283
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
295
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
284
296
  await this.cache.clear();
285
297
  }
286
- await (0, cache_1.clearSystemCache)();
298
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
299
+ await (0, cache_1.clearSystemCache)();
300
+ }
301
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
302
+ const updatedSchema = await (0, get_schema_1.getSchema)();
303
+ for (const nestedActionEvent of nestedActionEvents) {
304
+ nestedActionEvent.context.schema = updatedSchema;
305
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
306
+ }
307
+ }
287
308
  }
288
309
  }
289
- async updateField(collection, field) {
290
- var _a, _b;
310
+ async updateField(collection, field, opts) {
311
+ var _a, _b, _c;
291
312
  if (this.accountability && this.accountability.admin !== true) {
292
313
  throw new exceptions_1.ForbiddenException();
293
314
  }
294
315
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
316
+ const nestedActionEvents = [];
295
317
  try {
296
318
  const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
297
319
  keys: [field.field],
@@ -304,10 +326,10 @@ class FieldsService {
304
326
  const record = field.meta
305
327
  ? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
306
328
  : 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)) {
329
+ if (hookAdjustedField.type &&
330
+ (hookAdjustedField.type === 'alias' ||
331
+ ((_a = this.schema.collections[collection].fields[field.field]) === null || _a === void 0 ? void 0 : _a.type) === 'alias') &&
332
+ 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
333
  throw new exceptions_1.InvalidPayloadException('Alias type cannot be changed');
312
334
  }
313
335
  if (hookAdjustedField.schema) {
@@ -341,32 +363,52 @@ class FieldsService {
341
363
  }, { emitEvents: false });
342
364
  }
343
365
  }
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,
352
- });
366
+ const actionEvent = {
367
+ event: 'fields.update',
368
+ meta: {
369
+ payload: hookAdjustedField,
370
+ keys: [hookAdjustedField.field],
371
+ collection: collection,
372
+ },
373
+ context: {
374
+ database: (0, database_1.default)(),
375
+ schema: this.schema,
376
+ accountability: this.accountability,
377
+ },
378
+ };
379
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
380
+ opts.bypassEmitAction(actionEvent);
381
+ }
382
+ else {
383
+ nestedActionEvents.push(actionEvent);
384
+ }
353
385
  return field.field;
354
386
  }
355
387
  finally {
356
388
  if (runPostColumnChange) {
357
389
  await this.helpers.schema.postColumnChange();
358
390
  }
359
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
391
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
360
392
  await this.cache.clear();
361
393
  }
362
- await (0, cache_1.clearSystemCache)();
394
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
395
+ await (0, cache_1.clearSystemCache)();
396
+ }
397
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
398
+ const updatedSchema = await (0, get_schema_1.getSchema)();
399
+ for (const nestedActionEvent of nestedActionEvents) {
400
+ nestedActionEvent.context.schema = updatedSchema;
401
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
402
+ }
403
+ }
363
404
  }
364
405
  }
365
- async deleteField(collection, field) {
406
+ async deleteField(collection, field, opts) {
366
407
  if (this.accountability && this.accountability.admin !== true) {
367
408
  throw new exceptions_1.ForbiddenException();
368
409
  }
369
410
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
411
+ const nestedActionEvents = [];
370
412
  try {
371
413
  await emitter_1.default.emitFilter('fields.delete', [field], {
372
414
  collection: collection,
@@ -396,12 +438,19 @@ class FieldsService {
396
438
  const isM2O = relation.collection === collection && relation.field === field;
397
439
  // If the current field is a m2o, delete the related o2m if it exists and remove the relationship
398
440
  if (isM2O) {
399
- await relationsService.deleteOne(collection, field);
441
+ await relationsService.deleteOne(collection, field, {
442
+ autoPurgeSystemCache: false,
443
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
444
+ });
400
445
  if (relation.related_collection &&
401
446
  ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) &&
402
447
  relation.related_collection !== collection &&
403
448
  relation.meta.one_field !== field) {
404
- await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
449
+ await fieldsService.deleteField(relation.related_collection, relation.meta.one_field, {
450
+ autoPurgeCache: false,
451
+ autoPurgeSystemCache: false,
452
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
453
+ });
405
454
  }
406
455
  }
407
456
  // If the current field is a o2m, just delete the one field config from the relation
@@ -448,27 +497,46 @@ class FieldsService {
448
497
  }
449
498
  await trx('directus_fields').delete().where({ collection, field });
450
499
  });
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
- });
500
+ const actionEvent = {
501
+ event: 'fields.delete',
502
+ meta: {
503
+ payload: [field],
504
+ collection: collection,
505
+ },
506
+ context: {
507
+ database: this.knex,
508
+ schema: this.schema,
509
+ accountability: this.accountability,
510
+ },
511
+ };
512
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
513
+ opts.bypassEmitAction(actionEvent);
514
+ }
515
+ else {
516
+ nestedActionEvents.push(actionEvent);
517
+ }
459
518
  }
460
519
  finally {
461
520
  if (runPostColumnChange) {
462
521
  await this.helpers.schema.postColumnChange();
463
522
  }
464
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
523
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
465
524
  await this.cache.clear();
466
525
  }
467
- await (0, cache_1.clearSystemCache)();
526
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
527
+ await (0, cache_1.clearSystemCache)();
528
+ }
529
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
530
+ const updatedSchema = await (0, get_schema_1.getSchema)();
531
+ for (const nestedActionEvent of nestedActionEvents) {
532
+ nestedActionEvent.context.schema = updatedSchema;
533
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
534
+ }
535
+ }
468
536
  }
469
537
  }
470
538
  addColumnToTable(table, field, alter = null) {
471
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
539
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
472
540
  let column;
473
541
  // Don't attempt to add a DB column for alias / corrupt fields
474
542
  if (field.type === 'alias' || field.type === 'unknown')
@@ -487,7 +555,7 @@ class FieldsService {
487
555
  }
488
556
  else if (['float', 'decimal'].includes(field.type)) {
489
557
  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);
558
+ 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
559
  }
492
560
  else if (field.type === 'csv') {
493
561
  column = table.string(field.field);
@@ -510,7 +578,7 @@ class FieldsService {
510
578
  else {
511
579
  throw new exceptions_1.InvalidPayloadException(`Illegal type passed: "${field.type}"`);
512
580
  }
513
- if (((_f = field.schema) === null || _f === void 0 ? void 0 : _f.default_value) !== undefined) {
581
+ if (((_h = field.schema) === null || _h === void 0 ? void 0 : _h.default_value) !== undefined) {
514
582
  if (typeof field.schema.default_value === 'string' &&
515
583
  (field.schema.default_value.toLowerCase() === 'now()' || field.schema.default_value === 'CURRENT_TIMESTAMP')) {
516
584
  column.defaultTo(this.knex.fn.now());
@@ -525,7 +593,7 @@ class FieldsService {
525
593
  column.defaultTo(field.schema.default_value);
526
594
  }
527
595
  }
528
- if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) === false) {
596
+ if (((_j = field.schema) === null || _j === void 0 ? void 0 : _j.is_nullable) === false) {
529
597
  if (!alter || alter.is_nullable === true) {
530
598
  column.notNullable();
531
599
  }
@@ -535,15 +603,15 @@ class FieldsService {
535
603
  column.nullable();
536
604
  }
537
605
  }
538
- if ((_h = field.schema) === null || _h === void 0 ? void 0 : _h.is_primary_key) {
606
+ if ((_k = field.schema) === null || _k === void 0 ? void 0 : _k.is_primary_key) {
539
607
  column.primary().notNullable();
540
608
  }
541
- else if (((_j = field.schema) === null || _j === void 0 ? void 0 : _j.is_unique) === true) {
609
+ else if (((_l = field.schema) === null || _l === void 0 ? void 0 : _l.is_unique) === true) {
542
610
  if (!alter || alter.is_unique === false) {
543
611
  column.unique();
544
612
  }
545
613
  }
546
- else if (((_k = field.schema) === null || _k === void 0 ? void 0 : _k.is_unique) === false) {
614
+ else if (((_m = field.schema) === null || _m === void 0 ? void 0 : _m.is_unique) === false) {
547
615
  if (alter && alter.is_unique === true) {
548
616
  table.dropUnique([field.field]);
549
617
  }
@@ -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;