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
@@ -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;
@@ -193,19 +193,34 @@ class ItemsService {
193
193
  * Create multiple new items at once. Inserts all provided records sequentially wrapped in a transaction.
194
194
  */
195
195
  async createMany(data, opts) {
196
- const primaryKeys = await this.knex.transaction(async (trx) => {
196
+ const { primaryKeys, nestedActionEvents } = await this.knex.transaction(async (trx) => {
197
197
  const service = new ItemsService(this.collection, {
198
198
  accountability: this.accountability,
199
199
  schema: this.schema,
200
200
  knex: trx,
201
201
  });
202
202
  const primaryKeys = [];
203
+ const nestedActionEvents = [];
203
204
  for (const payload of data) {
204
- const primaryKey = await service.createOne(payload, { ...(opts || {}), autoPurgeCache: false });
205
+ const primaryKey = await service.createOne(payload, {
206
+ ...(opts || {}),
207
+ autoPurgeCache: false,
208
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
209
+ });
205
210
  primaryKeys.push(primaryKey);
206
211
  }
207
- return primaryKeys;
212
+ return { primaryKeys, nestedActionEvents };
208
213
  });
214
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
215
+ for (const nestedActionEvent of nestedActionEvents) {
216
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
217
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
218
+ }
219
+ else {
220
+ opts.bypassEmitAction(nestedActionEvent);
221
+ }
222
+ }
223
+ }
209
224
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
210
225
  await this.cache.clear();
211
226
  }
@@ -325,20 +340,31 @@ class ItemsService {
325
340
  * Update multiple items in a single transaction
326
341
  */
327
342
  async updateBatch(data, opts) {
343
+ if (!Array.isArray(data)) {
344
+ throw new exceptions_1.InvalidPayloadException('Input should be an array of items.');
345
+ }
328
346
  const primaryKeyField = this.schema.collections[this.collection].primary;
329
347
  const keys = [];
330
- await this.knex.transaction(async (trx) => {
331
- const service = new ItemsService(this.collection, {
332
- accountability: this.accountability,
333
- knex: trx,
334
- schema: this.schema,
348
+ try {
349
+ await this.knex.transaction(async (trx) => {
350
+ const service = new ItemsService(this.collection, {
351
+ accountability: this.accountability,
352
+ knex: trx,
353
+ schema: this.schema,
354
+ });
355
+ for (const item of data) {
356
+ if (!item[primaryKeyField])
357
+ throw new exceptions_1.InvalidPayloadException(`Item in update misses primary key.`);
358
+ const combinedOpts = Object.assign({ autoPurgeCache: false }, opts);
359
+ keys.push(await service.updateOne(item[primaryKeyField], (0, lodash_1.omit)(item, primaryKeyField), combinedOpts));
360
+ }
335
361
  });
336
- for (const item of data) {
337
- if (!item[primaryKeyField])
338
- throw new exceptions_1.InvalidPayloadException(`Item in update misses primary key.`);
339
- keys.push(await service.updateOne(item[primaryKeyField], (0, lodash_1.omit)(item, primaryKeyField), opts));
362
+ }
363
+ finally {
364
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
365
+ await this.cache.clear();
340
366
  }
341
- });
367
+ }
342
368
  return keys;
343
369
  }
344
370
  /**
@@ -2,7 +2,7 @@ import { Knex } from 'knex';
2
2
  import { AbstractServiceOptions } from '../../types';
3
3
  import { Accountability, SchemaOverview } from '@directus/shared/types';
4
4
  import { Transporter, SendMailOptions } from 'nodemailer';
5
- export declare type EmailOptions = SendMailOptions & {
5
+ export type EmailOptions = SendMailOptions & {
6
6
  template?: {
7
7
  name: string;
8
8
  data: Record<string, any>;
@@ -14,7 +14,7 @@ export declare class MailService {
14
14
  knex: Knex;
15
15
  mailer: Transporter;
16
16
  constructor(opts: AbstractServiceOptions);
17
- send(options: EmailOptions): Promise<void>;
17
+ send<T>(options: EmailOptions): Promise<T>;
18
18
  private renderTemplate;
19
19
  private getDefaultTemplateData;
20
20
  }
@@ -52,7 +52,8 @@ class MailService {
52
52
  .map((line) => line.trim())
53
53
  .join('\n');
54
54
  }
55
- await this.mailer.sendMail({ ...emailOptions, from, html });
55
+ const info = await this.mailer.sendMail({ ...emailOptions, from, html });
56
+ return info;
56
57
  }
57
58
  async renderTemplate(template, variables) {
58
59
  const customTemplatePath = path_1.default.resolve(env_1.default.EXTENSIONS_PATH, 'templates', template + '.liquid');
@@ -125,14 +125,14 @@ blockquote > p {
125
125
  <tbody>
126
126
  <tr>
127
127
  <td align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:0 0 30px 0">
128
- <table><tbody><tr><td align="center" valign="middle" style="background-color:{{ projectColor }};width:48px;height:48px;border-radius:4px;padding:6px;">
129
- <img id="logo" src="{{ projectLogo }}" alt="{{ projectName }} Logo" width="40" height="auto" border="0" style="-ms-interpolation-mode:bicubic; border:0; height:auto; line-height:100%; outline:none; text-decoration:none; display:block; width:40px;object-fit:contain;">
128
+ <table><tbody><tr><td align="center" valign="middle" style="background-color:{{ projectColor }};max-width:48px;max-height:48px;border-radius:4px;padding:6px;">
129
+ <img id="logo" src="{{ projectLogo }}" alt="{{ projectName }} Logo" width="40" height="auto" border="0" style="-ms-interpolation-mode:bicubic; border:0; height:40px; line-height:100%; outline:none; text-decoration:none; display:block; width:40px; object-fit:contain;">
130
130
  </td></tr></tbody></table>
131
131
  </td>
132
132
  </tr>
133
133
  <tr>
134
134
  <td id="content" align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:40px 50px 50px 50px; font-family:Open Sans, Helvetica, Arial, sans-serif; border-radius:4px; box-shadow:0 4px 0 #15253A; background-color:#FFFFFE; color:#172940; font-size:15px; line-height:26px; margin:0" bgcolor="#FFFFFE">
135
- <div id="hs_cos_wrapper_email_template_main_email_body" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module">
135
+ <div style="color: inherit; font-size: inherit; line-height: inherit;">
136
136
 
137
137
  {% block content %}{{ html }}{% endblock %}
138
138
 
@@ -142,7 +142,7 @@ blockquote > p {
142
142
  <tr>
143
143
  <td align="center" valign="middle" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:25px 0; font-family:Open Sans, Helvetica, Arial, sans-serif; color:#FFFFFE">
144
144
  <p style="margin-bottom: 1em; color: #A2B5CD;font-size: 12px; line-height: 16px;">
145
- Sent by the team at {{ projectName }} — <a style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; text-decoration:none; color:#A2B5CD" class="unsubscribe" data-unsubscribe="true" href="{{ url }}" data-hs-link-id="0" target="_blank">Manage Emails</a><br>
145
+ Sent by the team at {{ projectName }}{% if url %} — <a style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; text-decoration:none; color:#A2B5CD" href="{{ url }}" target="_blank">Manage Your Account</a>{% endif %}<br>
146
146
  {% block footer %}{% endblock %}
147
147
  </p>
148
148
  </td>
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.NotificationsService = void 0;
7
7
  const items_1 = require("./items");
8
8
  const md_1 = require("../utils/md");
9
+ const url_1 = require("../utils/url");
9
10
  const users_1 = require("./users");
10
11
  const mail_1 = require("./mail");
11
12
  const logger_1 = __importDefault(require("../logger"));
13
+ const env_1 = __importDefault(require("../env"));
12
14
  class NotificationsService extends items_1.ItemsService {
13
15
  constructor(options) {
14
16
  super('directus_notifications', options);
@@ -28,16 +30,19 @@ class NotificationsService extends items_1.ItemsService {
28
30
  return response;
29
31
  }
30
32
  async sendEmail(data) {
33
+ var _a;
31
34
  if (data.recipient) {
32
- const user = await this.usersService.readOne(data.recipient, { fields: ['email', 'email_notifications'] });
35
+ const user = await this.usersService.readOne(data.recipient, {
36
+ fields: ['id', 'email', 'email_notifications', 'role.app_access'],
37
+ });
38
+ const manageUserAccountUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'users', user.id).toString();
39
+ const html = data.message ? (0, md_1.md)(data.message) : '';
33
40
  if (user.email && user.email_notifications === true) {
34
41
  try {
35
42
  await this.mailService.send({
36
43
  template: {
37
44
  name: 'base',
38
- data: {
39
- html: data.message ? (0, md_1.md)(data.message) : '',
40
- },
45
+ data: ((_a = user.role) === null || _a === void 0 ? void 0 : _a.app_access) ? { url: manageUserAccountUrl, html } : { html },
41
46
  },
42
47
  to: user.email,
43
48
  subject: data.subject,
@@ -0,0 +1 @@
1
+ export {};
@@ -2,8 +2,8 @@ import { Accountability, SchemaOverview } from '@directus/shared/types';
2
2
  import { Knex } from 'knex';
3
3
  import { Helpers } from '../database/helpers';
4
4
  import { AbstractServiceOptions, ActionEventParams, Item, MutationOptions, PrimaryKey } from '../types';
5
- declare type Action = 'create' | 'read' | 'update';
6
- declare type Transformers = {
5
+ type Action = 'create' | 'read' | 'update';
6
+ type Transformers = {
7
7
  [type: string]: (context: {
8
8
  action: Action;
9
9
  value: any;
@@ -102,7 +102,9 @@ class PayloadService {
102
102
  async 'cast-csv'({ action, value }) {
103
103
  if (Array.isArray(value) === false && typeof value !== 'string')
104
104
  return;
105
- if (action === 'read' && Array.isArray(value) === false) {
105
+ if (action === 'read') {
106
+ if (Array.isArray(value))
107
+ return value;
106
108
  if (value === '')
107
109
  return [];
108
110
  return value.split(',');
@@ -346,7 +348,7 @@ class PayloadService {
346
348
  if (Object.keys(fieldsToUpdate).length > 0) {
347
349
  await itemsService.updateOne(relatedPrimaryKey, relatedRecord, {
348
350
  onRevisionCreate: (pk) => revisions.push(pk),
349
- bypassEmitAction: (params) => nestedActionEvents.push(params),
351
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
350
352
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
351
353
  });
352
354
  }
@@ -354,7 +356,7 @@ class PayloadService {
354
356
  else {
355
357
  relatedPrimaryKey = await itemsService.createOne(relatedRecord, {
356
358
  onRevisionCreate: (pk) => revisions.push(pk),
357
- bypassEmitAction: (params) => nestedActionEvents.push(params),
359
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
358
360
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
359
361
  });
360
362
  }
@@ -406,7 +408,7 @@ class PayloadService {
406
408
  if (Object.keys(fieldsToUpdate).length > 0) {
407
409
  await itemsService.updateOne(relatedPrimaryKey, relatedRecord, {
408
410
  onRevisionCreate: (pk) => revisions.push(pk),
409
- bypassEmitAction: (params) => nestedActionEvents.push(params),
411
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
410
412
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
411
413
  });
412
414
  }
@@ -414,7 +416,7 @@ class PayloadService {
414
416
  else {
415
417
  relatedPrimaryKey = await itemsService.createOne(relatedRecord, {
416
418
  onRevisionCreate: (pk) => revisions.push(pk),
417
- bypassEmitAction: (params) => nestedActionEvents.push(params),
419
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
418
420
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
419
421
  });
420
422
  }
@@ -496,7 +498,7 @@ class PayloadService {
496
498
  }
497
499
  savedPrimaryKeys.push(...(await itemsService.upsertMany(recordsToUpsert, {
498
500
  onRevisionCreate: (pk) => revisions.push(pk),
499
- bypassEmitAction: (params) => nestedActionEvents.push(params),
501
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
500
502
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
501
503
  })));
502
504
  const query = {
@@ -519,14 +521,14 @@ class PayloadService {
519
521
  if (relation.meta.one_deselect_action === 'delete') {
520
522
  // There's no revision for a deletion
521
523
  await itemsService.deleteByQuery(query, {
522
- bypassEmitAction: (params) => nestedActionEvents.push(params),
524
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
523
525
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
524
526
  });
525
527
  }
526
528
  else {
527
529
  await itemsService.updateByQuery(query, { [relation.field]: null }, {
528
530
  onRevisionCreate: (pk) => revisions.push(pk),
529
- bypassEmitAction: (params) => nestedActionEvents.push(params),
531
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
530
532
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
531
533
  });
532
534
  }
@@ -567,7 +569,7 @@ class PayloadService {
567
569
  }
568
570
  await itemsService.createMany(createPayload, {
569
571
  onRevisionCreate: (pk) => revisions.push(pk),
570
- bypassEmitAction: (params) => nestedActionEvents.push(params),
572
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
571
573
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
572
574
  });
573
575
  }
@@ -579,7 +581,7 @@ class PayloadService {
579
581
  [relation.field]: parent || payload[currentPrimaryKeyField],
580
582
  }, {
581
583
  onRevisionCreate: (pk) => revisions.push(pk),
582
- bypassEmitAction: (params) => nestedActionEvents.push(params),
584
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
583
585
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
584
586
  });
585
587
  }
@@ -603,14 +605,14 @@ class PayloadService {
603
605
  };
604
606
  if (relation.meta.one_deselect_action === 'delete') {
605
607
  await itemsService.deleteByQuery(query, {
606
- bypassEmitAction: (params) => nestedActionEvents.push(params),
608
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
607
609
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
608
610
  });
609
611
  }
610
612
  else {
611
613
  await itemsService.updateByQuery(query, { [relation.field]: null }, {
612
614
  onRevisionCreate: (pk) => revisions.push(pk),
613
- bypassEmitAction: (params) => nestedActionEvents.push(params),
615
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
614
616
  emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
615
617
  });
616
618
  }
@@ -4,7 +4,7 @@ import { ItemsService, QueryOptions } from './items';
4
4
  import { PermissionsService } from './permissions';
5
5
  import SchemaInspector from '@directus/schema';
6
6
  import Keyv from 'keyv';
7
- import { AbstractServiceOptions } from '../types';
7
+ import { AbstractServiceOptions, MutationOptions } from '../types';
8
8
  import { Helpers } from '../database/helpers';
9
9
  export declare class RelationsService {
10
10
  knex: Knex;
@@ -21,17 +21,17 @@ export declare class RelationsService {
21
21
  /**
22
22
  * Create a new relationship / foreign key constraint
23
23
  */
24
- createOne(relation: Partial<Relation>): Promise<void>;
24
+ createOne(relation: Partial<Relation>, opts?: MutationOptions): Promise<void>;
25
25
  /**
26
26
  * Update an existing foreign key constraint
27
27
  *
28
28
  * Note: You can update anything under meta, but only the `on_delete` trigger under schema
29
29
  */
30
- updateOne(collection: string, field: string, relation: Partial<Relation>): Promise<void>;
30
+ updateOne(collection: string, field: string, relation: Partial<Relation>, opts?: MutationOptions): Promise<void>;
31
31
  /**
32
32
  * Delete an existing relationship
33
33
  */
34
- deleteOne(collection: string, field: string): Promise<void>;
34
+ deleteOne(collection: string, field: string, opts?: MutationOptions): Promise<void>;
35
35
  /**
36
36
  * Whether or not the current user has read access to relations
37
37
  */
@@ -37,6 +37,8 @@ const database_1 = __importStar(require("../database"));
37
37
  const get_default_index_name_1 = require("../utils/get-default-index-name");
38
38
  const cache_1 = require("../cache");
39
39
  const helpers_1 = require("../database/helpers");
40
+ const emitter_1 = __importDefault(require("../emitter"));
41
+ const get_schema_1 = require("../utils/get-schema");
40
42
  class RelationsService {
41
43
  constructor(options) {
42
44
  this.knex = options.knex || (0, database_1.default)();
@@ -125,7 +127,7 @@ class RelationsService {
125
127
  /**
126
128
  * Create a new relationship / foreign key constraint
127
129
  */
128
- async createOne(relation) {
130
+ async createOne(relation, opts) {
129
131
  if (this.accountability && this.accountability.admin !== true) {
130
132
  throw new exceptions_1.ForbiddenException();
131
133
  }
@@ -153,6 +155,7 @@ class RelationsService {
153
155
  throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
154
156
  }
155
157
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
158
+ const nestedActionEvents = [];
156
159
  try {
157
160
  const metaRow = {
158
161
  ...(relation.meta || {}),
@@ -181,14 +184,25 @@ class RelationsService {
181
184
  // allowed to extract the relations regardless of permissions to directus_relations. This
182
185
  // happens in `filterForbidden` down below
183
186
  });
184
- await relationsItemService.createOne(metaRow);
187
+ await relationsItemService.createOne(metaRow, {
188
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
189
+ });
185
190
  });
186
191
  }
187
192
  finally {
188
193
  if (runPostColumnChange) {
189
194
  await this.helpers.schema.postColumnChange();
190
195
  }
191
- await (0, cache_1.clearSystemCache)();
196
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
197
+ await (0, cache_1.clearSystemCache)();
198
+ }
199
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
200
+ const updatedSchema = await (0, get_schema_1.getSchema)();
201
+ for (const nestedActionEvent of nestedActionEvents) {
202
+ nestedActionEvent.context.schema = updatedSchema;
203
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
204
+ }
205
+ }
192
206
  }
193
207
  }
194
208
  /**
@@ -196,7 +210,7 @@ class RelationsService {
196
210
  *
197
211
  * Note: You can update anything under meta, but only the `on_delete` trigger under schema
198
212
  */
199
- async updateOne(collection, field, relation) {
213
+ async updateOne(collection, field, relation, opts) {
200
214
  if (this.accountability && this.accountability.admin !== true) {
201
215
  throw new exceptions_1.ForbiddenException();
202
216
  }
@@ -211,6 +225,7 @@ class RelationsService {
211
225
  throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
212
226
  }
213
227
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
228
+ const nestedActionEvents = [];
214
229
  try {
215
230
  await this.knex.transaction(async (trx) => {
216
231
  if (existingRelation.related_collection) {
@@ -221,6 +236,8 @@ class RelationsService {
221
236
  if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.schema) {
222
237
  constraintName = existingRelation.schema.constraint_name || constraintName;
223
238
  table.dropForeign(field, constraintName);
239
+ constraintName = this.helpers.schema.constraintName(constraintName);
240
+ existingRelation.schema.constraint_name = constraintName;
224
241
  }
225
242
  this.alterType(table, relation);
226
243
  const builder = table
@@ -240,7 +257,9 @@ class RelationsService {
240
257
  });
241
258
  if (relation.meta) {
242
259
  if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
243
- await relationsItemService.updateOne(existingRelation.meta.id, relation.meta);
260
+ await relationsItemService.updateOne(existingRelation.meta.id, relation.meta, {
261
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
262
+ });
244
263
  }
245
264
  else {
246
265
  await relationsItemService.createOne({
@@ -248,6 +267,8 @@ class RelationsService {
248
267
  many_collection: relation.collection,
249
268
  many_field: relation.field,
250
269
  one_collection: existingRelation.related_collection || null,
270
+ }, {
271
+ bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
251
272
  });
252
273
  }
253
274
  }
@@ -257,13 +278,22 @@ class RelationsService {
257
278
  if (runPostColumnChange) {
258
279
  await this.helpers.schema.postColumnChange();
259
280
  }
260
- await (0, cache_1.clearSystemCache)();
281
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
282
+ await (0, cache_1.clearSystemCache)();
283
+ }
284
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
285
+ const updatedSchema = await (0, get_schema_1.getSchema)();
286
+ for (const nestedActionEvent of nestedActionEvents) {
287
+ nestedActionEvent.context.schema = updatedSchema;
288
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
289
+ }
290
+ }
261
291
  }
262
292
  }
263
293
  /**
264
294
  * Delete an existing relationship
265
295
  */
266
- async deleteOne(collection, field) {
296
+ async deleteOne(collection, field, opts) {
267
297
  if (this.accountability && this.accountability.admin !== true) {
268
298
  throw new exceptions_1.ForbiddenException();
269
299
  }
@@ -278,6 +308,7 @@ class RelationsService {
278
308
  throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
279
309
  }
280
310
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
311
+ const nestedActionEvents = [];
281
312
  try {
282
313
  await this.knex.transaction(async (trx) => {
283
314
  var _a;
@@ -292,13 +323,40 @@ class RelationsService {
292
323
  if (existingRelation.meta) {
293
324
  await trx('directus_relations').delete().where({ many_collection: collection, many_field: field });
294
325
  }
326
+ const actionEvent = {
327
+ event: 'relations.delete',
328
+ meta: {
329
+ payload: [field],
330
+ collection: collection,
331
+ },
332
+ context: {
333
+ database: this.knex,
334
+ schema: this.schema,
335
+ accountability: this.accountability,
336
+ },
337
+ };
338
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
339
+ opts.bypassEmitAction(actionEvent);
340
+ }
341
+ else {
342
+ nestedActionEvents.push(actionEvent);
343
+ }
295
344
  });
296
345
  }
297
346
  finally {
298
347
  if (runPostColumnChange) {
299
348
  await this.helpers.schema.postColumnChange();
300
349
  }
301
- await (0, cache_1.clearSystemCache)();
350
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
351
+ await (0, cache_1.clearSystemCache)();
352
+ }
353
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
354
+ const updatedSchema = await (0, get_schema_1.getSchema)();
355
+ for (const nestedActionEvent of nestedActionEvents) {
356
+ nestedActionEvent.context.schema = updatedSchema;
357
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
358
+ }
359
+ }
302
360
  }
303
361
  }
304
362
  /**
@@ -203,10 +203,16 @@ class UsersService extends items_1.ItemsService {
203
203
  throw new exceptions_2.InvalidPayloadException(`You can't change the "tfa_secret" value manually.`);
204
204
  }
205
205
  if (data.provider !== undefined) {
206
- throw new exceptions_2.InvalidPayloadException(`You can't change the "provider" value manually.`);
206
+ if (this.accountability && this.accountability.admin !== true) {
207
+ throw new exceptions_2.InvalidPayloadException(`You can't change the "provider" value manually.`);
208
+ }
209
+ data.auth_data = null;
207
210
  }
208
211
  if (data.external_identifier !== undefined) {
209
- throw new exceptions_2.InvalidPayloadException(`You can't change the "external_identifier" value manually.`);
212
+ if (this.accountability && this.accountability.admin !== true) {
213
+ throw new exceptions_2.InvalidPayloadException(`You can't change the "external_identifier" value manually.`);
214
+ }
215
+ data.auth_data = null;
210
216
  }
211
217
  return await super.updateMany(keys, data, opts);
212
218
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,20 +1,20 @@
1
1
  import { ResizeOptions, Sharp } from 'sharp';
2
2
  export declare const TransformationMethods: readonly ["toFormat", "jpeg", "png", "tiff", "webp", "resize", "extend", "extract", "trim", "rotate", "flip", "flop", "sharpen", "median", "blur", "flatten", "gamma", "negate", "normalise", "normalize", "clahe", "convolve", "threshold", "linear", "recomb", "modulate", "tint", "greyscale", "grayscale", "toColorspace", "toColourspace", "removeAlpha", "ensureAlpha", "extractChannel", "bandbool"];
3
- declare type AllowedSharpMethods = Pick<Sharp, typeof TransformationMethods[number]>;
4
- export declare type TransformationMap = {
3
+ type AllowedSharpMethods = Pick<Sharp, typeof TransformationMethods[number]>;
4
+ export type TransformationMap = {
5
5
  [M in keyof AllowedSharpMethods]: readonly [M, ...Parameters<AllowedSharpMethods[M]>];
6
6
  };
7
- export declare type Transformation = TransformationMap[keyof TransformationMap];
8
- export declare type TransformationParams = {
7
+ export type Transformation = TransformationMap[keyof TransformationMap];
8
+ export type TransformationParams = {
9
9
  key?: string;
10
10
  transforms?: Transformation[];
11
11
  };
12
- export declare type TransformationPreset = TransformationPresetFormat & TransformationPresetResize & TransformationParams & {
12
+ export type TransformationPreset = TransformationPresetFormat & TransformationPresetResize & TransformationParams & {
13
13
  key: string;
14
14
  };
15
- export declare type TransformationPresetFormat = {
15
+ export type TransformationPresetFormat = {
16
16
  format?: 'jpg' | 'jpeg' | 'png' | 'webp' | 'tiff';
17
17
  quality?: number;
18
18
  };
19
- export declare type TransformationPresetResize = Pick<ResizeOptions, 'width' | 'height' | 'fit' | 'withoutEnlargement'>;
19
+ export type TransformationPresetResize = Pick<ResizeOptions, 'width' | 'height' | 'fit' | 'withoutEnlargement'>;
20
20
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Query, Relation } from '@directus/shared/types';
2
- export declare type M2ONode = {
2
+ export type M2ONode = {
3
3
  type: 'm2o';
4
4
  name: string;
5
5
  children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
@@ -9,7 +9,7 @@ export declare type M2ONode = {
9
9
  parentKey: string;
10
10
  relatedKey: string;
11
11
  };
12
- export declare type A2MNode = {
12
+ export type A2MNode = {
13
13
  type: 'a2o';
14
14
  names: string[];
15
15
  children: {
@@ -25,7 +25,7 @@ export declare type A2MNode = {
25
25
  relation: Relation;
26
26
  parentKey: string;
27
27
  };
28
- export declare type O2MNode = {
28
+ export type O2MNode = {
29
29
  type: 'o2m';
30
30
  name: string;
31
31
  children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
@@ -35,20 +35,20 @@ export declare type O2MNode = {
35
35
  parentKey: string;
36
36
  relatedKey: string;
37
37
  };
38
- export declare type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
39
- export declare type FieldNode = {
38
+ export type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
39
+ export type FieldNode = {
40
40
  type: 'field';
41
41
  name: string;
42
42
  fieldKey: string;
43
43
  };
44
- export declare type FunctionFieldNode = {
44
+ export type FunctionFieldNode = {
45
45
  type: 'functionField';
46
46
  name: string;
47
47
  fieldKey: string;
48
48
  query: Query;
49
49
  relatedCollection: string;
50
50
  };
51
- export declare type AST = {
51
+ export type AST = {
52
52
  type: 'root';
53
53
  name: string;
54
54
  children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];