directus 9.4.2 → 9.5.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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/app.js +9 -0
  3. package/dist/auth/auth.d.ts +2 -1
  4. package/dist/auth/drivers/oauth2.js +10 -20
  5. package/dist/auth/drivers/openid.js +18 -8
  6. package/dist/cli/commands/init/index.js +8 -0
  7. package/dist/cli/commands/init/questions.d.ts +3 -0
  8. package/dist/cli/commands/init/questions.js +2 -0
  9. package/dist/cli/index.js +1 -1
  10. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  11. package/dist/cli/utils/create-db-connection.js +11 -1
  12. package/dist/cli/utils/drivers.d.ts +1 -0
  13. package/dist/cli/utils/drivers.js +2 -1
  14. package/dist/controllers/extensions.js +1 -1
  15. package/dist/controllers/files.js +3 -0
  16. package/dist/controllers/utils.js +2 -0
  17. package/dist/database/helpers/date/index.d.ts +1 -0
  18. package/dist/database/helpers/date/index.js +3 -1
  19. package/dist/database/helpers/geometry/index.d.ts +1 -0
  20. package/dist/database/helpers/geometry/index.js +3 -1
  21. package/dist/database/helpers/index.d.ts +2 -0
  22. package/dist/database/helpers/index.js +2 -0
  23. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +16 -0
  24. package/dist/database/helpers/schema/dialects/cockroachdb.js +16 -0
  25. package/dist/database/helpers/schema/dialects/default.d.ts +3 -0
  26. package/dist/database/helpers/schema/dialects/default.js +7 -0
  27. package/dist/database/helpers/schema/dialects/oracle.d.ts +12 -0
  28. package/dist/database/helpers/schema/dialects/oracle.js +13 -0
  29. package/dist/database/helpers/schema/index.d.ts +7 -0
  30. package/dist/database/helpers/schema/index.js +17 -0
  31. package/dist/database/helpers/schema/types.d.ts +25 -0
  32. package/dist/database/helpers/schema/types.js +89 -0
  33. package/dist/database/index.d.ts +1 -1
  34. package/dist/database/index.js +66 -20
  35. package/dist/database/migrations/20201105B-change-webhook-url-type.js +6 -25
  36. package/dist/database/migrations/20210312A-webhooks-collections-text.js +6 -25
  37. package/dist/database/migrations/20210415A-make-filesize-nullable.js +9 -4
  38. package/dist/database/migrations/20210506A-rename-interfaces.js +1 -1
  39. package/dist/database/migrations/20210510A-restructure-relations.js +12 -4
  40. package/dist/database/migrations/20210525A-add-insights.js +2 -2
  41. package/dist/database/migrations/20210626A-change-filesize-bigint.js +5 -7
  42. package/dist/database/migrations/20210903A-add-auth-provider.js +11 -2
  43. package/dist/database/migrations/20210907A-webhooks-collections-not-null.js +6 -20
  44. package/dist/database/migrations/20210920A-webhooks-url-not-null.js +10 -14
  45. package/dist/database/migrations/20211211A-add-shares.js +2 -2
  46. package/dist/database/migrations/run.js +1 -1
  47. package/dist/database/run-ast.d.ts +1 -1
  48. package/dist/database/seeds/01-collections.yaml +1 -0
  49. package/dist/database/seeds/02-roles.yaml +1 -0
  50. package/dist/database/seeds/03-users.yaml +1 -0
  51. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +8 -2
  52. package/dist/database/system-data/fields/collections.yaml +2 -0
  53. package/dist/database/system-data/relations/index.d.ts +1 -1
  54. package/dist/emitter.d.ts +3 -4
  55. package/dist/emitter.js +2 -8
  56. package/dist/env.js +1 -0
  57. package/dist/exceptions/database/translate.js +1 -0
  58. package/dist/exceptions/index.d.ts +1 -0
  59. package/dist/exceptions/index.js +1 -0
  60. package/dist/exceptions/unsupported-media-type.d.ts +4 -0
  61. package/dist/exceptions/unsupported-media-type.js +10 -0
  62. package/dist/extensions.d.ts +14 -8
  63. package/dist/extensions.js +136 -69
  64. package/dist/logger.js +22 -1
  65. package/dist/middleware/extract-token.js +1 -1
  66. package/dist/services/assets.js +3 -3
  67. package/dist/services/authentication.d.ts +2 -2
  68. package/dist/services/authentication.js +7 -2
  69. package/dist/services/authorization.d.ts +2 -3
  70. package/dist/services/collections.d.ts +2 -2
  71. package/dist/services/collections.js +20 -18
  72. package/dist/services/fields.d.ts +2 -3
  73. package/dist/services/fields.js +14 -10
  74. package/dist/services/graphql.d.ts +2 -1
  75. package/dist/services/graphql.js +4 -4
  76. package/dist/services/import.d.ts +2 -2
  77. package/dist/services/import.js +2 -1
  78. package/dist/services/items.d.ts +2 -2
  79. package/dist/services/items.js +22 -18
  80. package/dist/services/mail/index.d.ts +2 -2
  81. package/dist/services/meta.d.ts +2 -2
  82. package/dist/services/payload.d.ts +2 -2
  83. package/dist/services/payload.js +7 -3
  84. package/dist/services/relations.d.ts +2 -2
  85. package/dist/services/relations.js +4 -0
  86. package/dist/services/server.d.ts +2 -2
  87. package/dist/services/specifications.d.ts +2 -2
  88. package/dist/services/users.d.ts +2 -3
  89. package/dist/services/utils.d.ts +2 -2
  90. package/dist/types/ast.d.ts +1 -2
  91. package/dist/types/auth.d.ts +1 -3
  92. package/dist/types/index.d.ts +0 -3
  93. package/dist/types/index.js +0 -3
  94. package/dist/types/services.d.ts +1 -3
  95. package/dist/types/snapshot.d.ts +1 -2
  96. package/dist/utils/apply-query.d.ts +1 -2
  97. package/dist/utils/apply-query.js +1 -1
  98. package/dist/utils/apply-snapshot.d.ts +2 -1
  99. package/dist/utils/get-ast-from-query.d.ts +2 -3
  100. package/dist/utils/get-local-type.js +1 -1
  101. package/dist/utils/get-permissions.d.ts +1 -2
  102. package/dist/utils/get-permissions.js +1 -1
  103. package/dist/utils/get-relation-type.d.ts +1 -1
  104. package/dist/utils/get-schema.d.ts +1 -2
  105. package/dist/utils/get-snapshot.d.ts +2 -1
  106. package/dist/utils/md.js +1 -1
  107. package/dist/utils/merge-permissions-for-share.d.ts +1 -2
  108. package/dist/utils/reduce-schema.d.ts +1 -2
  109. package/example.env +8 -0
  110. package/package.json +23 -17
  111. package/dist/cli/index.test.d.ts +0 -1
  112. package/dist/cli/index.test.js +0 -58
  113. package/dist/middleware/cache.test.d.ts +0 -1
  114. package/dist/middleware/cache.test.js +0 -62
  115. package/dist/tests/database/migrations/run.test.d.ts +0 -1
  116. package/dist/tests/database/migrations/run.test.js +0 -29
  117. package/dist/types/extensions.d.ts +0 -43
  118. package/dist/types/extensions.js +0 -2
  119. package/dist/types/relation.d.ts +0 -21
  120. package/dist/types/relation.js +0 -2
  121. package/dist/types/schema.d.ts +0 -32
  122. package/dist/types/schema.js +0 -2
  123. package/dist/utils/get-cache-key.test.d.ts +0 -1
  124. package/dist/utils/get-cache-key.test.js +0 -53
@@ -66,24 +66,7 @@ class CollectionsService {
66
66
  // permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
67
67
  // transactions.
68
68
  await this.knex.transaction(async (trx) => {
69
- if (payload.meta) {
70
- const collectionItemsService = new items_1.ItemsService('directus_collections', {
71
- knex: trx,
72
- accountability: this.accountability,
73
- schema: this.schema,
74
- });
75
- await collectionItemsService.createOne({
76
- ...payload.meta,
77
- collection: payload.collection,
78
- });
79
- }
80
69
  if (payload.schema) {
81
- const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
82
- const fieldItemsService = new items_1.ItemsService('directus_fields', {
83
- knex: trx,
84
- accountability: this.accountability,
85
- schema: this.schema,
86
- });
87
70
  // Directus heavily relies on the primary key of a collection, so we have to make sure that
88
71
  // every collection that is created has a primary key. If no primary key field is created
89
72
  // while making the collection, we default to an auto incremented id named `id`
@@ -114,6 +97,7 @@ class CollectionsService {
114
97
  }
115
98
  return field;
116
99
  });
100
+ const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
117
101
  await trx.schema.createTable(payload.collection, (table) => {
118
102
  for (const field of payload.fields) {
119
103
  if (field.type && constants_1.ALIAS_TYPES.includes(field.type) === false) {
@@ -121,9 +105,25 @@ class CollectionsService {
121
105
  }
122
106
  }
123
107
  });
108
+ const fieldItemsService = new items_1.ItemsService('directus_fields', {
109
+ knex: trx,
110
+ accountability: this.accountability,
111
+ schema: this.schema,
112
+ });
124
113
  const fieldPayloads = payload.fields.filter((field) => field.meta).map((field) => field.meta);
125
114
  await fieldItemsService.createMany(fieldPayloads);
126
115
  }
116
+ if (payload.meta) {
117
+ const collectionItemsService = new items_1.ItemsService('directus_collections', {
118
+ knex: trx,
119
+ accountability: this.accountability,
120
+ schema: this.schema,
121
+ });
122
+ await collectionItemsService.createOne({
123
+ ...payload.meta,
124
+ collection: payload.collection,
125
+ });
126
+ }
127
127
  return payload.collection;
128
128
  });
129
129
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
@@ -316,6 +316,9 @@ class CollectionsService {
316
316
  }
317
317
  await this.knex.transaction(async (trx) => {
318
318
  var _a;
319
+ if (collectionToBeDeleted.schema) {
320
+ await trx.schema.dropTable(collectionKey);
321
+ }
319
322
  // Make sure this collection isn't used as a group in any other collections
320
323
  await trx('directus_collections').update({ group: null }).where({ group: collectionKey });
321
324
  if (collectionToBeDeleted.meta) {
@@ -371,7 +374,6 @@ class CollectionsService {
371
374
  .update({ one_allowed_collections: newAllowedCollections })
372
375
  .where({ id: relation.meta.id });
373
376
  }
374
- await trx.schema.dropTable(collectionKey);
375
377
  }
376
378
  });
377
379
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
@@ -3,9 +3,8 @@ import { Knex } from 'knex';
3
3
  import { Column } from 'knex-schema-inspector/dist/types/column';
4
4
  import { ItemsService } from '../services/items';
5
5
  import { PayloadService } from '../services/payload';
6
- import { AbstractServiceOptions, SchemaOverview } from '../types';
7
- import { Accountability } from '@directus/shared/types';
8
- import { Field, RawField, Type } from '@directus/shared/types';
6
+ import { AbstractServiceOptions } from '../types';
7
+ import { Field, RawField, Type, Accountability, SchemaOverview } from '@directus/shared/types';
9
8
  import { Helpers } from '../database/helpers';
10
9
  import Keyv from 'keyv';
11
10
  export declare class FieldsService {
@@ -325,6 +325,13 @@ class FieldsService {
325
325
  });
326
326
  await this.knex.transaction(async (trx) => {
327
327
  var _a, _b;
328
+ if (this.schema.collections[collection] &&
329
+ field in this.schema.collections[collection].fields &&
330
+ this.schema.collections[collection].fields[field].alias === false) {
331
+ await trx.schema.table(collection, (table) => {
332
+ table.dropColumn(field);
333
+ });
334
+ }
328
335
  const relations = this.schema.relations.filter((relation) => {
329
336
  var _a;
330
337
  return ((relation.collection === collection && relation.field === field) ||
@@ -384,13 +391,6 @@ class FieldsService {
384
391
  .where({ group: metaRow.field, collection: metaRow.collection });
385
392
  }
386
393
  await trx('directus_fields').delete().where({ collection, field });
387
- if (this.schema.collections[collection] &&
388
- field in this.schema.collections[collection].fields &&
389
- this.schema.collections[collection].fields[field].alias === false) {
390
- await trx.schema.table(collection, (table) => {
391
- table.dropColumn(field);
392
- });
393
- }
394
394
  });
395
395
  if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
396
396
  await this.cache.clear();
@@ -452,11 +452,15 @@ class FieldsService {
452
452
  column.defaultTo(field.schema.default_value);
453
453
  }
454
454
  }
455
- if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) !== undefined && field.schema.is_nullable === false) {
456
- column.notNullable();
455
+ if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) === false) {
456
+ if (!alter || alter.is_nullable === true) {
457
+ column.notNullable();
458
+ }
457
459
  }
458
460
  else {
459
- column.nullable();
461
+ if (!alter || alter.is_nullable === false) {
462
+ column.nullable();
463
+ }
460
464
  }
461
465
  if ((_h = field.schema) === null || _h === void 0 ? void 0 : _h.is_primary_key) {
462
466
  column.primary().notNullable();
@@ -1,9 +1,10 @@
1
1
  import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLScalarType, GraphQLSchema, ObjectFieldNode, SelectionNode } from 'graphql';
2
+ import { SchemaOverview } from '@directus/shared/types';
2
3
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
3
4
  import { Knex } from 'knex';
4
5
  import { BaseException } from '@directus/shared/exceptions';
5
6
  import { Accountability, Query } from '@directus/shared/types';
6
- import { AbstractServiceOptions, GraphQLParams, Item, SchemaOverview } from '../types';
7
+ import { AbstractServiceOptions, GraphQLParams, Item } from '../types';
7
8
  import { ItemsService } from './items';
8
9
  export declare const GraphQLGeoJSON: GraphQLScalarType;
9
10
  export declare const GraphQLDate: GraphQLScalarType;
@@ -1397,10 +1397,10 @@ class GraphQLService {
1397
1397
  resolve: async () => {
1398
1398
  const extensionManager = (0, extensions_1.getExtensionManager)();
1399
1399
  return {
1400
- interfaces: extensionManager.listExtensions('interface'),
1401
- displays: extensionManager.listExtensions('display'),
1402
- layouts: extensionManager.listExtensions('layout'),
1403
- modules: extensionManager.listExtensions('module'),
1400
+ interfaces: extensionManager.getExtensionsList('interface'),
1401
+ displays: extensionManager.getExtensionsList('display'),
1402
+ layouts: extensionManager.getExtensionsList('layout'),
1403
+ modules: extensionManager.getExtensionsList('module'),
1404
1404
  };
1405
1405
  },
1406
1406
  },
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Knex } from 'knex';
3
- import { AbstractServiceOptions, SchemaOverview } from '../types';
4
- import { Accountability } from '@directus/shared/types';
3
+ import { AbstractServiceOptions } from '../types';
4
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
5
5
  export declare class ImportService {
6
6
  knex: Knex;
7
7
  accountability: Accountability | null;
@@ -31,9 +31,10 @@ class ImportService {
31
31
  case 'application/json':
32
32
  return await this.importJSON(collection, stream);
33
33
  case 'text/csv':
34
+ case 'application/vnd.ms-excel':
34
35
  return await this.importCSV(collection, stream);
35
36
  default:
36
- throw new exceptions_1.InvalidPayloadException(`Can't import files of type "${mimetype}"`);
37
+ throw new exceptions_1.UnsupportedMediaTypeException(`Can't import files of type "${mimetype}"`);
37
38
  }
38
39
  }
39
40
  importJSON(collection, stream) {
@@ -1,7 +1,7 @@
1
1
  import { Knex } from 'knex';
2
2
  import Keyv from 'keyv';
3
- import { Accountability, Query, PermissionsAction } from '@directus/shared/types';
4
- import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
3
+ import { Accountability, Query, PermissionsAction, SchemaOverview } from '@directus/shared/types';
4
+ import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, MutationOptions } from '../types';
5
5
  export declare type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
@@ -70,7 +70,9 @@ class ItemsService {
70
70
  // Run all hooks that are attached to this event so the end user has the chance to augment the
71
71
  // item that is about to be saved
72
72
  const payloadAfterHooks = (opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false
73
- ? await emitter_1.default.emitFilter(`${this.eventScope}.create`, payload, {
73
+ ? await emitter_1.default.emitFilter(this.eventScope === 'items'
74
+ ? ['items.create', `${this.collection}.items.create`]
75
+ : `${this.eventScope}.create`, payload, {
74
76
  collection: this.collection,
75
77
  }, {
76
78
  database: trx,
@@ -85,7 +87,7 @@ class ItemsService {
85
87
  const { payload: payloadWithA2O, revisions: revisionsA2O } = await payloadService.processA2O(payloadWithM2O);
86
88
  const payloadWithoutAliases = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, ...aliases));
87
89
  const payloadWithTypeCasting = await payloadService.processValues('create', payloadWithoutAliases);
88
- // In case of manual string / UUID primary keys, they PK already exists in the object we're saving.
90
+ // In case of manual string / UUID primary keys, the PK already exists in the object we're saving.
89
91
  let primaryKey = payloadWithTypeCasting[primaryKeyField];
90
92
  try {
91
93
  const result = await trx.insert(payloadWithoutAliases).into(this.collection).returning(primaryKeyField);
@@ -146,14 +148,14 @@ class ItemsService {
146
148
  return primaryKey;
147
149
  });
148
150
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
149
- emitter_1.default.emitAction(`${this.eventScope}.create`, {
151
+ emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.create', `${this.collection}.items.create`] : `${this.eventScope}.create`, {
150
152
  payload,
151
153
  key: primaryKey,
152
154
  collection: this.collection,
153
155
  }, {
154
156
  // This hook is called async. If we would pass the transaction here, the hook can be
155
157
  // called after the transaction is done #5460
156
- database: (0, database_1.default)(),
158
+ database: this.knex || (0, database_1.default)(),
157
159
  schema: this.schema,
158
160
  accountability: this.accountability,
159
161
  });
@@ -189,11 +191,6 @@ class ItemsService {
189
191
  * Get items by query
190
192
  */
191
193
  async readByQuery(query, opts) {
192
- const authorizationService = new authorization_1.AuthorizationService({
193
- accountability: this.accountability,
194
- knex: this.knex,
195
- schema: this.schema,
196
- });
197
194
  let ast = await (0, get_ast_from_query_1.default)(this.collection, query, this.schema, {
198
195
  accountability: this.accountability,
199
196
  // By setting the permissions action, you can read items using the permissions for another
@@ -203,6 +200,11 @@ class ItemsService {
203
200
  knex: this.knex,
204
201
  });
205
202
  if (this.accountability && this.accountability.admin !== true) {
203
+ const authorizationService = new authorization_1.AuthorizationService({
204
+ accountability: this.accountability,
205
+ knex: this.knex,
206
+ schema: this.schema,
207
+ });
206
208
  ast = await authorizationService.processAST(ast, opts === null || opts === void 0 ? void 0 : opts.permissionsAction);
207
209
  }
208
210
  const records = await (0, run_ast_1.default)(ast, this.schema, {
@@ -213,7 +215,7 @@ class ItemsService {
213
215
  if (records === null) {
214
216
  throw new exceptions_1.ForbiddenException();
215
217
  }
216
- const filteredRecords = await emitter_1.default.emitFilter(`${this.eventScope}.read`, records, {
218
+ const filteredRecords = await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, records, {
217
219
  query,
218
220
  collection: this.collection,
219
221
  }, {
@@ -221,12 +223,12 @@ class ItemsService {
221
223
  schema: this.schema,
222
224
  accountability: this.accountability,
223
225
  });
224
- emitter_1.default.emitAction(`${this.eventScope}.read`, {
226
+ emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, {
225
227
  payload: filteredRecords,
226
228
  query,
227
229
  collection: this.collection,
228
230
  }, {
229
- database: (0, database_1.default)(),
231
+ database: this.knex || (0, database_1.default)(),
230
232
  schema: this.schema,
231
233
  accountability: this.accountability,
232
234
  });
@@ -288,7 +290,9 @@ class ItemsService {
288
290
  // Run all hooks that are attached to this event so the end user has the chance to augment the
289
291
  // item that is about to be saved
290
292
  const payloadAfterHooks = (opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false
291
- ? await emitter_1.default.emitFilter(`${this.eventScope}.update`, payload, {
293
+ ? await emitter_1.default.emitFilter(this.eventScope === 'items'
294
+ ? ['items.update', `${this.collection}.items.update`]
295
+ : `${this.eventScope}.update`, payload, {
292
296
  keys,
293
297
  collection: this.collection,
294
298
  }, {
@@ -379,14 +383,14 @@ class ItemsService {
379
383
  await this.cache.clear();
380
384
  }
381
385
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
382
- emitter_1.default.emitAction(`${this.eventScope}.update`, {
386
+ emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.update', `${this.collection}.items.update`] : `${this.eventScope}.update`, {
383
387
  payload,
384
388
  keys,
385
389
  collection: this.collection,
386
390
  }, {
387
391
  // This hook is called async. If we would pass the transaction here, the hook can be
388
392
  // called after the transaction is done #5460
389
- database: (0, database_1.default)(),
393
+ database: this.knex || (0, database_1.default)(),
390
394
  schema: this.schema,
391
395
  accountability: this.accountability,
392
396
  });
@@ -461,7 +465,7 @@ class ItemsService {
461
465
  await authorizationService.checkAccess('delete', this.collection, keys);
462
466
  }
463
467
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
464
- await emitter_1.default.emitFilter(`${this.eventScope}.delete`, keys, {
468
+ await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, keys, {
465
469
  collection: this.collection,
466
470
  }, {
467
471
  database: this.knex,
@@ -490,13 +494,13 @@ class ItemsService {
490
494
  await this.cache.clear();
491
495
  }
492
496
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
493
- emitter_1.default.emitAction(`${this.eventScope}.delete`, {
497
+ emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, {
494
498
  payload: keys,
495
499
  collection: this.collection,
496
500
  }, {
497
501
  // This hook is called async. If we would pass the transaction here, the hook can be
498
502
  // called after the transaction is done #5460
499
- database: (0, database_1.default)(),
503
+ database: this.knex || (0, database_1.default)(),
500
504
  schema: this.schema,
501
505
  accountability: this.accountability,
502
506
  });
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, SchemaOverview } from '../../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { AbstractServiceOptions } from '../../types';
3
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
4
4
  import { Transporter, SendMailOptions } from 'nodemailer';
5
5
  export declare type EmailOptions = SendMailOptions & {
6
6
  template?: {
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, SchemaOverview } from '../types';
3
- import { Accountability, Query } from '@directus/shared/types';
2
+ import { AbstractServiceOptions } from '../types';
3
+ import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
4
4
  export declare class MetaService {
5
5
  knex: Knex;
6
6
  accountability: Accountability | null;
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
3
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
4
4
  import { Helpers } from '../database/helpers';
5
5
  declare type Action = 'create' | 'read' | 'update';
6
6
  declare type Transformers = {
@@ -100,12 +100,16 @@ class PayloadService {
100
100
  return value;
101
101
  },
102
102
  async csv({ action, value }) {
103
- if (!value)
103
+ if (Array.isArray(value) === false && typeof value !== 'string')
104
104
  return;
105
- if (action === 'read' && Array.isArray(value) === false)
105
+ if (action === 'read' && Array.isArray(value) === false) {
106
+ if (value === '')
107
+ return [];
106
108
  return value.split(',');
107
- if (Array.isArray(value))
109
+ }
110
+ if (Array.isArray(value)) {
108
111
  return value.join(',');
112
+ }
109
113
  return value;
110
114
  },
111
115
  };
@@ -1,10 +1,10 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, SchemaOverview, Relation, RelationMeta } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { SchemaOverview, Relation, RelationMeta, Accountability } from '@directus/shared/types';
4
3
  import { ItemsService, QueryOptions } from './items';
5
4
  import { PermissionsService } from './permissions';
6
5
  import SchemaInspector from '@directus/schema';
7
6
  import Keyv from 'keyv';
7
+ import { AbstractServiceOptions } from '../types';
8
8
  export declare class RelationsService {
9
9
  knex: Knex;
10
10
  permissionsService: PermissionsService;
@@ -135,6 +135,10 @@ class RelationsService {
135
135
  if (relation.field in this.schema.collections[relation.collection].fields === false) {
136
136
  throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" doesn't exist in collection "${relation.collection}"`);
137
137
  }
138
+ // A primary key should not be a foreign key
139
+ if (this.schema.collections[relation.collection].primary === relation.field) {
140
+ throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" is a primary key`);
141
+ }
138
142
  if (relation.related_collection && relation.related_collection in this.schema.collections === false) {
139
143
  throw new exceptions_1.InvalidPayloadException(`Collection "${relation.related_collection}" doesn't exist`);
140
144
  }
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, SchemaOverview } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { AbstractServiceOptions } from '../types';
3
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
4
4
  import { SettingsService } from './settings';
5
5
  export declare class ServerService {
6
6
  knex: Knex;
@@ -1,7 +1,7 @@
1
1
  import { Knex } from 'knex';
2
2
  import { OpenAPIObject } from 'openapi3-ts';
3
- import { AbstractServiceOptions, SchemaOverview } from '../types';
4
- import { Accountability } from '@directus/shared/types';
3
+ import { AbstractServiceOptions } from '../types';
4
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
5
5
  import { CollectionsService } from './collections';
6
6
  import { FieldsService } from './fields';
7
7
  import { GraphQLService } from './graphql';
@@ -1,7 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, Item, PrimaryKey, SchemaOverview, MutationOptions } from '../types';
3
- import { Query } from '@directus/shared/types';
4
- import { Accountability } from '@directus/shared/types';
2
+ import { AbstractServiceOptions, Item, PrimaryKey, MutationOptions } from '../types';
3
+ import { Query, SchemaOverview, Accountability } from '@directus/shared/types';
5
4
  import { ItemsService } from './items';
6
5
  export declare class UsersService extends ItemsService {
7
6
  knex: Knex;
@@ -1,6 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { AbstractServiceOptions, PrimaryKey, SchemaOverview } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { AbstractServiceOptions, PrimaryKey } from '../types';
3
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
4
4
  export declare class UtilsService {
5
5
  knex: Knex;
6
6
  accountability: Accountability | null;
@@ -1,5 +1,4 @@
1
- import { Query } from '@directus/shared/types';
2
- import { Relation } from './relation';
1
+ import { Query, Relation } from '@directus/shared/types';
3
2
  export declare type M2ONode = {
4
3
  type: 'm2o';
5
4
  name: string;
@@ -1,5 +1,5 @@
1
+ import { SchemaOverview } from '@directus/shared/types';
1
2
  import { Knex } from 'knex';
2
- import { SchemaOverview } from './schema';
3
3
  export interface AuthDriverOptions {
4
4
  knex: Knex;
5
5
  schema: SchemaOverview;
@@ -22,10 +22,8 @@ export declare type AuthData = Record<string, any> | null;
22
22
  export interface Session {
23
23
  token: string;
24
24
  expires: Date;
25
- data: string | Record<string, unknown> | null;
26
25
  share: string;
27
26
  }
28
- export declare type SessionData = Record<string, any> | null;
29
27
  export declare type DirectusTokenPayload = {
30
28
  id?: string;
31
29
  role: string | null;
@@ -3,15 +3,12 @@ export * from './assets';
3
3
  export * from './ast';
4
4
  export * from './auth';
5
5
  export * from './collection';
6
- export * from './extensions';
7
6
  export * from './files';
8
7
  export * from './graphql';
9
8
  export * from './items';
10
9
  export * from './meta';
11
10
  export * from './migration';
12
- export * from './relation';
13
11
  export * from './revision';
14
- export * from './schema';
15
12
  export * from './services';
16
13
  export * from './snapshot';
17
14
  export * from './webhooks';
@@ -15,15 +15,12 @@ __exportStar(require("./assets"), exports);
15
15
  __exportStar(require("./ast"), exports);
16
16
  __exportStar(require("./auth"), exports);
17
17
  __exportStar(require("./collection"), exports);
18
- __exportStar(require("./extensions"), exports);
19
18
  __exportStar(require("./files"), exports);
20
19
  __exportStar(require("./graphql"), exports);
21
20
  __exportStar(require("./items"), exports);
22
21
  __exportStar(require("./meta"), exports);
23
22
  __exportStar(require("./migration"), exports);
24
- __exportStar(require("./relation"), exports);
25
23
  __exportStar(require("./revision"), exports);
26
- __exportStar(require("./schema"), exports);
27
24
  __exportStar(require("./services"), exports);
28
25
  __exportStar(require("./snapshot"), exports);
29
26
  __exportStar(require("./webhooks"), exports);
@@ -1,8 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { SchemaOverview } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
4
3
  import { Item, PrimaryKey } from './items';
5
- import { Query } from '@directus/shared/types';
6
4
  export declare type AbstractServiceOptions = {
7
5
  knex?: Knex;
8
6
  accountability?: Accountability | null;
@@ -1,6 +1,5 @@
1
1
  import { Collection } from './collection';
2
- import { Relation, RelationMeta } from './relation';
3
- import { Field, FieldMeta } from '@directus/shared/types';
2
+ import { Relation, RelationMeta, Field, FieldMeta } from '@directus/shared/types';
4
3
  import { Diff } from 'deep-diff';
5
4
  export declare type Snapshot = {
6
5
  version: number;
@@ -1,6 +1,5 @@
1
1
  import { Knex } from 'knex';
2
- import { SchemaOverview } from '../types';
3
- import { Aggregate, Filter, Query } from '@directus/shared/types';
2
+ import { Aggregate, Filter, Query, SchemaOverview } from '@directus/shared/types';
4
3
  /**
5
4
  * Apply the Query to a given Knex query builder instance
6
5
  */
@@ -177,7 +177,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
177
177
  if (relationType === 'o2m' && (subQuery === true || parentAlias !== undefined)) {
178
178
  dbQuery.leftJoin({ [alias]: relation.collection }, `${parentAlias || parentCollection}.${schema.collections[relation.related_collection].primary}`, `${alias}.${relation.field}`);
179
179
  }
180
- if (relationType === 'm2o' || subQuery === true) {
180
+ if (relationType === 'm2o' || subQuery === true || (relationType === 'o2m' && parentAlias !== undefined)) {
181
181
  let parent;
182
182
  if (relationType === 'm2o') {
183
183
  parent = relation.related_collection;
@@ -1,5 +1,6 @@
1
- import { Snapshot, SnapshotDiff, SchemaOverview } from '../types';
1
+ import { Snapshot, SnapshotDiff } from '../types';
2
2
  import { Knex } from 'knex';
3
+ import { SchemaOverview } from '@directus/shared/types';
3
4
  export declare function applySnapshot(snapshot: Snapshot, options?: {
4
5
  database?: Knex;
5
6
  schema?: SchemaOverview;
@@ -2,9 +2,8 @@
2
2
  * Generate an AST based on a given collection and query
3
3
  */
4
4
  import { Knex } from 'knex';
5
- import { Accountability } from '@directus/shared/types';
6
- import { AST, SchemaOverview } from '../types';
7
- import { Query, PermissionsAction } from '@directus/shared/types';
5
+ import { AST } from '../types';
6
+ import { Query, PermissionsAction, Accountability, SchemaOverview } from '@directus/shared/types';
8
7
  declare type GetASTOptions = {
9
8
  accountability?: Accountability | null;
10
9
  action?: PermissionsAction;
@@ -107,7 +107,7 @@ function getLocalType(column, field) {
107
107
  return 'hash';
108
108
  if (special.includes('csv'))
109
109
  return 'csv';
110
- if (special.includes('uuid'))
110
+ if (special.includes('uuid') || special.includes('file'))
111
111
  return 'uuid';
112
112
  if (type === null || type === void 0 ? void 0 : type.startsWith('geometry')) {
113
113
  return special[0] || 'geometry';
@@ -1,3 +1,2 @@
1
- import { Permission, Accountability } from '@directus/shared/types';
2
- import { SchemaOverview } from '../types';
1
+ import { Permission, Accountability, SchemaOverview } from '@directus/shared/types';
3
2
  export declare function getPermissions(accountability: Accountability, schema: SchemaOverview): Promise<Permission[]>;
@@ -145,7 +145,7 @@ function processPermissions(accountability, permissions, filterContext) {
145
145
  return permissions.map((permission) => {
146
146
  permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
147
147
  permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
148
- permission.presets = (0, utils_1.parseFilter)(permission.presets, accountability, filterContext);
148
+ permission.presets = (0, utils_1.parsePreset)(permission.presets, accountability, filterContext);
149
149
  return permission;
150
150
  });
151
151
  }
@@ -1,4 +1,4 @@
1
- import { Relation } from '../types';
1
+ import { Relation } from '@directus/shared/types';
2
2
  export declare function getRelationType(getRelationOptions: {
3
3
  relation: Relation;
4
4
  collection: string | null;
@@ -1,6 +1,5 @@
1
1
  import { Knex } from 'knex';
2
- import { SchemaOverview } from '../types';
3
- import { Accountability } from '@directus/shared/types';
2
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
4
3
  export declare function getSchema(options?: {
5
4
  accountability?: Accountability;
6
5
  database?: Knex;
@@ -1,5 +1,6 @@
1
- import { SchemaOverview, Snapshot } from '../types';
1
+ import { Snapshot } from '../types';
2
2
  import { Knex } from 'knex';
3
+ import { SchemaOverview } from '@directus/shared/types';
3
4
  export declare function getSnapshot(options?: {
4
5
  database?: Knex;
5
6
  schema?: SchemaOverview;