directus 9.12.0 → 9.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/app.js +11 -1
  2. package/dist/auth/drivers/index.js +5 -1
  3. package/dist/auth/drivers/ldap.js +5 -1
  4. package/dist/auth/drivers/oauth2.js +15 -23
  5. package/dist/auth/drivers/openid.js +16 -24
  6. package/dist/cli/commands/bootstrap/index.js +5 -1
  7. package/dist/cli/commands/schema/apply.js +7 -3
  8. package/dist/cli/commands/schema/snapshot.d.ts +1 -1
  9. package/dist/cli/commands/schema/snapshot.js +33 -25
  10. package/dist/cli/index.js +1 -1
  11. package/dist/cli/utils/create-env/env-stub.liquid +11 -11
  12. package/dist/constants.d.ts +1 -0
  13. package/dist/constants.js +5 -1
  14. package/dist/controllers/assets.js +5 -5
  15. package/dist/controllers/dashboards.js +4 -1
  16. package/dist/controllers/files.js +8 -5
  17. package/dist/controllers/flows.js +4 -1
  18. package/dist/controllers/folders.js +4 -1
  19. package/dist/controllers/items.js +4 -1
  20. package/dist/controllers/notifications.js +4 -1
  21. package/dist/controllers/operations.js +4 -1
  22. package/dist/controllers/panels.js +4 -1
  23. package/dist/controllers/permissions.js +4 -1
  24. package/dist/controllers/presets.js +4 -1
  25. package/dist/controllers/roles.js +4 -1
  26. package/dist/controllers/shares.js +4 -1
  27. package/dist/controllers/users.js +75 -3
  28. package/dist/controllers/utils.js +3 -3
  29. package/dist/database/helpers/date/dialects/sqlite.js +3 -0
  30. package/dist/database/helpers/fn/dialects/oracle.d.ts +9 -9
  31. package/dist/database/helpers/fn/dialects/oracle.js +22 -16
  32. package/dist/database/helpers/fn/dialects/sqlite.d.ts +9 -9
  33. package/dist/database/helpers/fn/dialects/sqlite.js +46 -16
  34. package/dist/database/helpers/fn/types.d.ts +12 -9
  35. package/dist/database/helpers/index.js +5 -1
  36. package/dist/database/index.js +2 -0
  37. package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -2
  38. package/dist/database/migrations/20210506A-rename-interfaces.js +2 -2
  39. package/dist/database/migrations/20210802A-replace-groups.js +2 -2
  40. package/dist/database/migrations/20210805A-update-groups.js +2 -2
  41. package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -3
  42. package/dist/database/migrations/20211007A-update-presets.js +5 -5
  43. package/dist/database/migrations/20220429A-add-flows.js +1 -2
  44. package/dist/database/migrations/20220614A-rename-hook-trigger-to-event.d.ts +3 -0
  45. package/dist/database/migrations/20220614A-rename-hook-trigger-to-event.js +11 -0
  46. package/dist/database/system-data/fields/dashboards.yaml +1 -0
  47. package/dist/database/system-data/fields/flows.yaml +1 -1
  48. package/dist/env.d.ts +1 -1
  49. package/dist/env.js +8 -3
  50. package/dist/exceptions/database/translate.js +5 -1
  51. package/dist/exceptions/index.js +5 -1
  52. package/dist/extensions.d.ts +2 -1
  53. package/dist/extensions.js +31 -23
  54. package/dist/flows.d.ts +5 -0
  55. package/dist/flows.js +66 -40
  56. package/dist/index.js +5 -1
  57. package/dist/logger.js +5 -1
  58. package/dist/messenger.js +2 -2
  59. package/dist/middleware/graphql.js +2 -2
  60. package/dist/middleware/respond.js +10 -1
  61. package/dist/middleware/validate-batch.js +3 -1
  62. package/dist/operations/item-create/index.js +1 -2
  63. package/dist/operations/item-delete/index.d.ts +1 -0
  64. package/dist/operations/item-delete/index.js +8 -7
  65. package/dist/operations/item-read/index.js +7 -6
  66. package/dist/operations/item-update/index.d.ts +1 -0
  67. package/dist/operations/item-update/index.js +9 -8
  68. package/dist/operations/log/index.js +1 -2
  69. package/dist/operations/notification/index.js +1 -2
  70. package/dist/operations/request/index.d.ts +4 -1
  71. package/dist/operations/request/index.js +5 -1
  72. package/dist/operations/transform/index.js +1 -2
  73. package/dist/operations/trigger/index.js +1 -2
  74. package/dist/server.js +5 -1
  75. package/dist/services/assets.js +5 -1
  76. package/dist/services/collections.js +5 -1
  77. package/dist/services/fields.d.ts +3 -3
  78. package/dist/services/fields.js +25 -17
  79. package/dist/services/files.js +5 -1
  80. package/dist/services/flows.d.ts +1 -2
  81. package/dist/services/flows.js +19 -8
  82. package/dist/services/{graphql.d.ts → graphql/index.d.ts} +3 -5
  83. package/dist/services/{graphql.js → graphql/index.js} +115 -102
  84. package/dist/services/graphql/types/date.d.ts +2 -0
  85. package/dist/services/graphql/types/date.js +9 -0
  86. package/dist/services/graphql/types/geojson.d.ts +2 -0
  87. package/dist/services/graphql/types/geojson.js +10 -0
  88. package/dist/services/graphql/types/string-or-float.d.ts +5 -0
  89. package/dist/services/graphql/types/string-or-float.js +34 -0
  90. package/dist/services/graphql/types/void.d.ts +2 -0
  91. package/dist/services/graphql/types/void.js +17 -0
  92. package/dist/services/graphql/utils/add-path-to-validation-error.d.ts +2 -0
  93. package/dist/services/graphql/utils/add-path-to-validation-error.js +20 -0
  94. package/dist/services/import-export.js +13 -10
  95. package/dist/services/index.js +5 -1
  96. package/dist/services/items.d.ts +5 -1
  97. package/dist/services/items.js +22 -2
  98. package/dist/services/mail/index.js +8 -6
  99. package/dist/services/operations.d.ts +1 -2
  100. package/dist/services/operations.js +19 -8
  101. package/dist/services/payload.js +2 -3
  102. package/dist/services/permissions.d.ts +1 -0
  103. package/dist/services/permissions.js +5 -0
  104. package/dist/services/relations.js +5 -1
  105. package/dist/services/roles.d.ts +1 -0
  106. package/dist/services/roles.js +9 -0
  107. package/dist/services/server.js +5 -1
  108. package/dist/services/users.d.ts +1 -0
  109. package/dist/services/users.js +22 -0
  110. package/dist/types/index.js +5 -1
  111. package/dist/utils/apply-query.js +24 -15
  112. package/dist/utils/apply-snapshot.js +3 -0
  113. package/dist/utils/calculate-field-depth.d.ts +33 -0
  114. package/dist/utils/calculate-field-depth.js +75 -0
  115. package/dist/utils/get-column.js +1 -1
  116. package/dist/utils/get-default-value.js +3 -13
  117. package/dist/utils/get-graphql-type.js +4 -3
  118. package/dist/utils/get-local-type.d.ts +6 -3
  119. package/dist/utils/get-permissions.js +3 -4
  120. package/dist/utils/get-schema.js +1 -2
  121. package/dist/utils/get-string-byte-size.d.ts +4 -0
  122. package/dist/utils/get-string-byte-size.js +10 -0
  123. package/dist/utils/job-queue.d.ts +9 -0
  124. package/dist/utils/job-queue.js +24 -0
  125. package/dist/utils/jwt.js +5 -1
  126. package/dist/utils/sanitize-query.js +4 -5
  127. package/dist/utils/validate-query.js +50 -0
  128. package/dist/webhooks.js +5 -1
  129. package/package.json +75 -74
  130. package/dist/utils/operation-options.d.ts +0 -3
  131. package/dist/utils/operation-options.js +0 -45
  132. package/dist/utils/parse-json.d.ts +0 -5
  133. package/dist/utils/parse-json.js +0 -19
@@ -14,10 +14,10 @@ const env_1 = __importDefault(require("../env"));
14
14
  const exceptions_1 = require("../exceptions");
15
15
  const translate_1 = require("../exceptions/database/translate");
16
16
  const get_ast_from_query_1 = __importDefault(require("../utils/get-ast-from-query"));
17
+ const validate_keys_1 = require("../utils/validate-keys");
17
18
  const authorization_1 = require("./authorization");
18
19
  const index_1 = require("./index");
19
20
  const payload_1 = require("./payload");
20
- const validate_keys_1 = require("../utils/validate-keys");
21
21
  class ItemsService {
22
22
  constructor(collection, options) {
23
23
  this.collection = collection;
@@ -289,7 +289,27 @@ class ItemsService {
289
289
  return key;
290
290
  }
291
291
  /**
292
- * Update many items by primary key
292
+ * Update multiple items in a single transaction
293
+ */
294
+ async updateBatch(data, opts) {
295
+ const primaryKeyField = this.schema.collections[this.collection].primary;
296
+ const keys = [];
297
+ await this.knex.transaction(async (trx) => {
298
+ const service = new ItemsService(this.collection, {
299
+ accountability: this.accountability,
300
+ knex: trx,
301
+ schema: this.schema,
302
+ });
303
+ for (const item of data) {
304
+ if (!item[primaryKeyField])
305
+ throw new exceptions_1.InvalidPayloadException(`Item in update misses primary key.`);
306
+ keys.push(await service.updateOne(item[primaryKeyField], (0, lodash_1.omit)(item, primaryKeyField), opts));
307
+ }
308
+ });
309
+ return keys;
310
+ }
311
+ /**
312
+ * Update many items by primary key, setting all items to the same change
293
313
  */
294
314
  async updateMany(keys, data, opts) {
295
315
  const primaryKeyField = this.schema.collections[this.collection].primary;
@@ -23,12 +23,14 @@ class MailService {
23
23
  this.accountability = opts.accountability || null;
24
24
  this.knex = (opts === null || opts === void 0 ? void 0 : opts.knex) || (0, database_1.default)();
25
25
  this.mailer = (0, mailer_1.default)();
26
- this.mailer.verify((error) => {
27
- if (error) {
28
- logger_1.default.warn(`Email connection failed:`);
29
- logger_1.default.warn(error);
30
- }
31
- });
26
+ if (env_1.default.EMAIL_VERIFY_SETUP) {
27
+ this.mailer.verify((error) => {
28
+ if (error) {
29
+ logger_1.default.warn(`Email connection failed:`);
30
+ logger_1.default.warn(error);
31
+ }
32
+ });
33
+ }
32
34
  }
33
35
  async send(options) {
34
36
  const { template, ...emailOptions } = options;
@@ -1,13 +1,12 @@
1
1
  import { OperationRaw } from '@directus/shared/types';
2
- import { Messenger } from '../messenger';
3
2
  import { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types';
4
3
  import { ItemsService } from './items';
5
4
  export declare class OperationsService extends ItemsService<OperationRaw> {
6
- messenger: Messenger;
7
5
  constructor(options: AbstractServiceOptions);
8
6
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
7
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
10
8
  updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
+ updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
11
10
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
12
11
  deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
13
12
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
@@ -1,41 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OperationsService = void 0;
4
- const messenger_1 = require("../messenger");
4
+ const flows_1 = require("../flows");
5
5
  const items_1 = require("./items");
6
6
  class OperationsService extends items_1.ItemsService {
7
7
  constructor(options) {
8
8
  super('directus_operations', options);
9
- this.messenger = (0, messenger_1.getMessenger)();
10
9
  }
11
10
  async createOne(data, opts) {
11
+ const flowManager = (0, flows_1.getFlowManager)();
12
12
  const result = await super.createOne(data, opts);
13
- this.messenger.publish('flows', { type: 'reload' });
13
+ await flowManager.reload();
14
14
  return result;
15
15
  }
16
16
  async createMany(data, opts) {
17
+ const flowManager = (0, flows_1.getFlowManager)();
17
18
  const result = await super.createMany(data, opts);
18
- this.messenger.publish('flows', { type: 'reload' });
19
+ await flowManager.reload();
19
20
  return result;
20
21
  }
21
22
  async updateOne(key, data, opts) {
23
+ const flowManager = (0, flows_1.getFlowManager)();
22
24
  const result = await super.updateOne(key, data, opts);
23
- this.messenger.publish('flows', { type: 'reload' });
25
+ await flowManager.reload();
26
+ return result;
27
+ }
28
+ async updateBatch(data, opts) {
29
+ const flowManager = (0, flows_1.getFlowManager)();
30
+ const result = await super.updateBatch(data, opts);
31
+ await flowManager.reload();
24
32
  return result;
25
33
  }
26
34
  async updateMany(keys, data, opts) {
35
+ const flowManager = (0, flows_1.getFlowManager)();
27
36
  const result = await super.updateMany(keys, data, opts);
28
- this.messenger.publish('flows', { type: 'reload' });
37
+ await flowManager.reload();
29
38
  return result;
30
39
  }
31
40
  async deleteOne(key, opts) {
41
+ const flowManager = (0, flows_1.getFlowManager)();
32
42
  const result = await super.deleteOne(key, opts);
33
- this.messenger.publish('flows', { type: 'reload' });
43
+ await flowManager.reload();
34
44
  return result;
35
45
  }
36
46
  async deleteMany(keys, opts) {
47
+ const flowManager = (0, flows_1.getFlowManager)();
37
48
  const result = await super.deleteMany(keys, opts);
38
- this.messenger.publish('flows', { type: 'reload' });
49
+ await flowManager.reload();
39
50
  return result;
40
51
  }
41
52
  }
@@ -15,7 +15,6 @@ const database_1 = __importDefault(require("../database"));
15
15
  const helpers_1 = require("../database/helpers");
16
16
  const exceptions_1 = require("../exceptions");
17
17
  const generate_hash_1 = require("../utils/generate-hash");
18
- const parse_json_1 = require("../utils/parse-json");
19
18
  const items_1 = require("./items");
20
19
  /**
21
20
  * Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
@@ -56,7 +55,7 @@ class PayloadService {
56
55
  if (action === 'read') {
57
56
  if (typeof value === 'string') {
58
57
  try {
59
- return (0, parse_json_1.parseJSON)(value);
58
+ return (0, utils_1.parseJSON)(value);
60
59
  }
61
60
  catch {
62
61
  return value;
@@ -197,7 +196,7 @@ class PayloadService {
197
196
  processGeometries(payloads, action) {
198
197
  const process = action == 'read'
199
198
  ? (value) => (typeof value === 'string' ? (0, wellknown_1.parse)(value) : value)
200
- : (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? (0, parse_json_1.parseJSON)(value) : value);
199
+ : (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? (0, utils_1.parseJSON)(value) : value);
201
200
  const fieldsInCollection = Object.entries(this.schema.collections[this.collection].fields);
202
201
  const geometryColumns = fieldsInCollection.filter(([_, field]) => field.type.startsWith('geometry'));
203
202
  for (const [name] of geometryColumns) {
@@ -12,6 +12,7 @@ export declare class PermissionsService extends ItemsService {
12
12
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
13
13
  updateByQuery(query: Query, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
14
14
  updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
15
+ updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
15
16
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
16
17
  upsertOne(payload: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
17
18
  upsertMany(payloads: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
@@ -70,6 +70,11 @@ class PermissionsService extends items_1.ItemsService {
70
70
  await (0, cache_1.clearSystemCache)();
71
71
  return res;
72
72
  }
73
+ async updateBatch(data, opts) {
74
+ const res = await super.updateBatch(data, opts);
75
+ await (0, cache_1.clearSystemCache)();
76
+ return res;
77
+ }
73
78
  async updateMany(keys, data, opts) {
74
79
  const res = await super.updateMany(keys, data, opts);
75
80
  await (0, cache_1.clearSystemCache)();
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -6,6 +6,7 @@ export declare class RolesService extends ItemsService {
6
6
  private checkForOtherAdminRoles;
7
7
  private checkForOtherAdminUsers;
8
8
  updateOne(key: PrimaryKey, data: Record<string, any>, opts?: MutationOptions): Promise<PrimaryKey>;
9
+ updateBatch(data: Record<string, any>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
9
10
  updateMany(keys: PrimaryKey[], data: Record<string, any>, opts?: MutationOptions): Promise<PrimaryKey[]>;
10
11
  deleteOne(key: PrimaryKey): Promise<PrimaryKey>;
11
12
  deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
@@ -64,6 +64,15 @@ class RolesService extends items_1.ItemsService {
64
64
  }
65
65
  return super.updateOne(key, data, opts);
66
66
  }
67
+ async updateBatch(data, opts) {
68
+ const primaryKeyField = this.schema.collections[this.collection].primary;
69
+ const keys = data.map((item) => item[primaryKeyField]);
70
+ const setsToNoAdmin = data.some((item) => item.admin_access === false);
71
+ if (setsToNoAdmin) {
72
+ await this.checkForOtherAdminRoles(keys);
73
+ }
74
+ return super.updateBatch(data, opts);
75
+ }
67
76
  async updateMany(keys, data, opts) {
68
77
  if ('admin_access' in data && data.admin_access === false) {
69
78
  await this.checkForOtherAdminRoles(keys);
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -38,6 +38,7 @@ export declare class UsersService extends ItemsService {
38
38
  * Update a single user by primary key
39
39
  */
40
40
  updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
41
+ updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
41
42
  /**
42
43
  * Update many users by primary key
43
44
  */
@@ -153,6 +153,23 @@ class UsersService extends items_1.ItemsService {
153
153
  await this.updateMany([key], data, opts);
154
154
  return key;
155
155
  }
156
+ async updateBatch(data, opts) {
157
+ const primaryKeyField = this.schema.collections[this.collection].primary;
158
+ const keys = [];
159
+ await this.knex.transaction(async (trx) => {
160
+ const service = new UsersService({
161
+ accountability: this.accountability,
162
+ knex: trx,
163
+ schema: this.schema,
164
+ });
165
+ for (const item of data) {
166
+ if (!item[primaryKeyField])
167
+ throw new exceptions_2.InvalidPayloadException(`User in update misses primary key.`);
168
+ keys.push(await service.updateOne(item[primaryKeyField], item, opts));
169
+ }
170
+ });
171
+ return keys;
172
+ }
156
173
  /**
157
174
  * Update many users by primary key
158
175
  */
@@ -305,6 +322,7 @@ class UsersService extends items_1.ItemsService {
305
322
  await (0, stall_1.stall)(STALL_TIME, timeStart);
306
323
  }
307
324
  async resetPassword(token, password) {
325
+ var _a;
308
326
  const { email, scope, hash } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
309
327
  if (scope !== 'password-reset' || !hash)
310
328
  throw new exceptions_2.ForbiddenException();
@@ -317,6 +335,10 @@ class UsersService extends items_1.ItemsService {
317
335
  const service = new UsersService({
318
336
  knex: this.knex,
319
337
  schema: this.schema,
338
+ accountability: {
339
+ ...((_a = this.accountability) !== null && _a !== void 0 ? _a : { role: null }),
340
+ admin: true, // We need to skip permissions checks for the update call below
341
+ },
320
342
  });
321
343
  await service.updateOne(user.id, { password, status: 'active' });
322
344
  }
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -211,7 +211,15 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
211
211
  const columnName = (0, get_column_path_1.getColumnPath)({ path: filterPath, collection, relations, aliasMap });
212
212
  if (!columnName)
213
213
  continue;
214
- applyFilterToQuery(columnName, filterOperator, filterValue, logical);
214
+ if (relation === null || relation === void 0 ? void 0 : relation.related_collection) {
215
+ applyFilterToQuery(columnName, filterOperator, filterValue, logical, relation.related_collection); // m2o
216
+ }
217
+ else if (filterPath[0].includes(':')) {
218
+ applyFilterToQuery(columnName, filterOperator, filterValue, logical, filterPath[0].split(':')[1]); // a2o
219
+ }
220
+ else {
221
+ applyFilterToQuery(columnName, filterOperator, filterValue, logical);
222
+ }
215
223
  }
216
224
  else {
217
225
  applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
@@ -245,7 +253,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
245
253
  }
246
254
  }
247
255
  }
248
- function applyFilterToQuery(key, operator, compareValue, logical = 'and') {
256
+ function applyFilterToQuery(key, operator, compareValue, logical = 'and', originalCollectionName) {
249
257
  const [table, column] = key.split('.');
250
258
  // Is processed through Knex.Raw, so should be safe to string-inject into these where queries
251
259
  const selectionRaw = (0, get_column_1.getColumn)(knex, table, column, false, schema);
@@ -268,6 +276,17 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
268
276
  query.where(key, '!=', '');
269
277
  });
270
278
  }
279
+ // The following fields however, require a value to be run. If no value is passed, we
280
+ // ignore them. This allows easier use in GraphQL, where you wouldn't be able to
281
+ // conditionally build out your filter structure (#4471)
282
+ if (compareValue === undefined)
283
+ return;
284
+ if (Array.isArray(compareValue)) {
285
+ // Tip: when using a `[Type]` type in GraphQL, but don't provide the variable, it'll be
286
+ // reported as [undefined].
287
+ // We need to remove any undefined values, as they are useless
288
+ compareValue = compareValue.filter((val) => val !== undefined);
289
+ }
271
290
  // Cast filter value (compareValue) based on function used
272
291
  if (column.includes('(') && column.includes(')')) {
273
292
  const functionName = column.split('(')[0];
@@ -278,8 +297,9 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
278
297
  }
279
298
  // Cast filter value (compareValue) based on type of field being filtered against
280
299
  const [collection, field] = key.split('.');
281
- if (collection in schema.collections && field in schema.collections[collection].fields) {
282
- const type = schema.collections[collection].fields[field].type;
300
+ const mappedCollection = originalCollectionName || collection;
301
+ if (mappedCollection in schema.collections && field in schema.collections[mappedCollection].fields) {
302
+ const type = schema.collections[mappedCollection].fields[field].type;
283
303
  if (['date', 'dateTime', 'time', 'timestamp'].includes(type)) {
284
304
  if (Array.isArray(compareValue)) {
285
305
  compareValue = compareValue.map((val) => helpers.date.parse(val));
@@ -297,17 +317,6 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
297
317
  }
298
318
  }
299
319
  }
300
- // The following fields however, require a value to be run. If no value is passed, we
301
- // ignore them. This allows easier use in GraphQL, where you wouldn't be able to
302
- // conditionally build out your filter structure (#4471)
303
- if (compareValue === undefined)
304
- return;
305
- if (Array.isArray(compareValue)) {
306
- // Tip: when using a `[Type]` type in GraphQL, but don't provide the variable, it'll be
307
- // reported as [undefined].
308
- // We need to remove any undefined values, as they are useless
309
- compareValue = compareValue.filter((val) => val !== undefined);
310
- }
311
320
  if (operator === '_eq') {
312
321
  dbQuery[logical].where(selectionRaw, '=', compareValue);
313
322
  }
@@ -11,10 +11,12 @@ const services_1 = require("../services");
11
11
  const get_schema_1 = require("./get-schema");
12
12
  const get_snapshot_1 = require("./get-snapshot");
13
13
  const get_snapshot_diff_1 = require("./get-snapshot-diff");
14
+ const cache_1 = require("../cache");
14
15
  async function applySnapshot(snapshot, options) {
15
16
  var _a, _b, _c, _d;
16
17
  const database = (_a = options === null || options === void 0 ? void 0 : options.database) !== null && _a !== void 0 ? _a : (0, database_1.default)();
17
18
  const schema = (_b = options === null || options === void 0 ? void 0 : options.schema) !== null && _b !== void 0 ? _b : (await (0, get_schema_1.getSchema)({ database }));
19
+ const { systemCache } = (0, cache_1.getCache)();
18
20
  const current = (_c = options === null || options === void 0 ? void 0 : options.current) !== null && _c !== void 0 ? _c : (await (0, get_snapshot_1.getSnapshot)({ database, schema }));
19
21
  const snapshotDiff = (_d = options === null || options === void 0 ? void 0 : options.diff) !== null && _d !== void 0 ? _d : (0, get_snapshot_diff_1.getSnapshotDiff)(current, snapshot);
20
22
  await database.transaction(async (trx) => {
@@ -178,6 +180,7 @@ async function applySnapshot(snapshot, options) {
178
180
  }
179
181
  }
180
182
  });
183
+ await (systemCache === null || systemCache === void 0 ? void 0 : systemCache.clear());
181
184
  }
182
185
  exports.applySnapshot = applySnapshot;
183
186
  function isNestedMetaUpdate(diff) {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Calculates the depth of a given JSON structure, not counting any _ prefixed properties
3
+ *
4
+ * Used to calculate the field depth in a filter or deep query structure
5
+ *
6
+ * @example
7
+ *
8
+ * ```js
9
+ * const deep = {
10
+ * translations: {
11
+ * _filter: {
12
+ * _and: [
13
+ * {
14
+ * language_id: {
15
+ * name: {
16
+ * _eq: 'English'
17
+ * }
18
+ * }
19
+ * },
20
+ * {
21
+ * status: {
22
+ * _eq: 'Published'
23
+ * }
24
+ * }
25
+ * ]
26
+ * }
27
+ * }
28
+ * };
29
+ *
30
+ * const result = calculateFieldDepth(deep); // => 3
31
+ * ```
32
+ */
33
+ export declare function calculateFieldDepth(obj?: Record<string, any> | null, dotNotationKeys?: string[]): number;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateFieldDepth = void 0;
4
+ const lodash_1 = require("lodash");
5
+ /**
6
+ * Calculates the depth of a given JSON structure, not counting any _ prefixed properties
7
+ *
8
+ * Used to calculate the field depth in a filter or deep query structure
9
+ *
10
+ * @example
11
+ *
12
+ * ```js
13
+ * const deep = {
14
+ * translations: {
15
+ * _filter: {
16
+ * _and: [
17
+ * {
18
+ * language_id: {
19
+ * name: {
20
+ * _eq: 'English'
21
+ * }
22
+ * }
23
+ * },
24
+ * {
25
+ * status: {
26
+ * _eq: 'Published'
27
+ * }
28
+ * }
29
+ * ]
30
+ * }
31
+ * }
32
+ * };
33
+ *
34
+ * const result = calculateFieldDepth(deep); // => 3
35
+ * ```
36
+ */
37
+ function calculateFieldDepth(obj, dotNotationKeys = []) {
38
+ if (!obj) {
39
+ return 0;
40
+ }
41
+ let depth = 0;
42
+ const keys = Object.keys(obj);
43
+ for (const key of keys) {
44
+ const nestedValue = obj[key];
45
+ if (dotNotationKeys.includes(key) && nestedValue) {
46
+ let sortDepth = 0;
47
+ for (const sortKey of nestedValue) {
48
+ if (sortKey) {
49
+ sortDepth = Math.max(sortKey.split('.').length, sortDepth);
50
+ }
51
+ }
52
+ if (sortDepth > depth) {
53
+ depth = sortDepth;
54
+ }
55
+ }
56
+ else {
57
+ if (!(0, lodash_1.isPlainObject)(nestedValue) && !(0, lodash_1.isArray)(nestedValue))
58
+ continue;
59
+ let nestedDepth = 0;
60
+ if (Array.isArray(nestedValue)) {
61
+ nestedDepth = Math.max(...nestedValue.map((val) => calculateFieldDepth(val, dotNotationKeys)));
62
+ }
63
+ else {
64
+ nestedDepth = calculateFieldDepth(nestedValue, dotNotationKeys);
65
+ }
66
+ if (key.startsWith('_') === false)
67
+ nestedDepth += 1;
68
+ if (nestedDepth > depth) {
69
+ depth = nestedDepth;
70
+ }
71
+ }
72
+ }
73
+ return depth;
74
+ }
75
+ exports.calculateFieldDepth = calculateFieldDepth;
@@ -28,7 +28,7 @@ function getColumn(knex, table, column, alias = (0, apply_function_to_column_nam
28
28
  if (allowedFunctions.includes(functionName) === false) {
29
29
  throw new exceptions_1.InvalidQueryException(`Invalid function specified "${functionName}"`);
30
30
  }
31
- const result = fn[functionName](table, columnName);
31
+ const result = fn[functionName](table, columnName, { type });
32
32
  if (alias) {
33
33
  return knex.raw(result + ' AS ??', [alias]);
34
34
  }
@@ -3,26 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const utils_1 = require("@directus/shared/utils");
6
7
  const env_1 = __importDefault(require("../env"));
7
8
  const logger_1 = __importDefault(require("../logger"));
8
9
  const get_local_type_1 = __importDefault(require("./get-local-type"));
9
- const parse_json_1 = require("./parse-json");
10
10
  function getDefaultValue(column) {
11
11
  var _a;
12
12
  const type = (0, get_local_type_1.default)(column);
13
- let defaultValue = (_a = column.default_value) !== null && _a !== void 0 ? _a : null;
13
+ const defaultValue = (_a = column.default_value) !== null && _a !== void 0 ? _a : null;
14
14
  if (defaultValue === null)
15
15
  return null;
16
- if (defaultValue === 'null')
17
- return null;
18
- if (defaultValue === 'NULL')
19
- return null;
20
- // Check if the default is wrapped in an extra pair of quotes, this happens in SQLite / MariaDB
21
- if (typeof defaultValue === 'string' &&
22
- ((defaultValue.startsWith(`'`) && defaultValue.endsWith(`'`)) ||
23
- (defaultValue.startsWith(`"`) && defaultValue.endsWith(`"`)))) {
24
- defaultValue = defaultValue.slice(1, -1);
25
- }
26
16
  if (defaultValue === '0000-00-00 00:00:00')
27
17
  return null;
28
18
  switch (type) {
@@ -60,7 +50,7 @@ function castToObject(value) {
60
50
  return value;
61
51
  if (typeof value === 'string') {
62
52
  try {
63
- return (0, parse_json_1.parseJSON)(value);
53
+ return (0, utils_1.parseJSON)(value);
64
54
  }
65
55
  catch (err) {
66
56
  if (env_1.default.NODE_ENV === 'development') {
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getGraphQLType = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  const graphql_compose_1 = require("graphql-compose");
6
- const graphql_2 = require("../services/graphql");
6
+ const date_1 = require("../services/graphql/types/date");
7
+ const geojson_1 = require("../services/graphql/types/geojson");
7
8
  function getGraphQLType(localType) {
8
9
  switch (localType) {
9
10
  case 'boolean':
@@ -20,11 +21,11 @@ function getGraphQLType(localType) {
20
21
  case 'json':
21
22
  return graphql_compose_1.GraphQLJSON;
22
23
  case 'geometry':
23
- return graphql_2.GraphQLGeoJSON;
24
+ return geojson_1.GraphQLGeoJSON;
24
25
  case 'timestamp':
25
26
  case 'dateTime':
26
27
  case 'date':
27
- return graphql_2.GraphQLDate;
28
+ return date_1.GraphQLDate;
28
29
  default:
29
30
  return graphql_1.GraphQLString;
30
31
  }
@@ -1,6 +1,9 @@
1
- import { SchemaOverview } from '@directus/schema/dist/types/overview';
2
- import { Column } from 'knex-schema-inspector/dist/types/column';
3
1
  import { FieldMeta, Type } from '@directus/shared/types';
4
- export default function getLocalType(column?: SchemaOverview[string]['columns'][string] | Column, field?: {
2
+ export default function getLocalType(column?: {
3
+ data_type: string;
4
+ numeric_precision?: null | number;
5
+ numeric_scale?: null | number;
6
+ max_length?: null | number;
7
+ }, field?: {
5
8
  special?: FieldMeta['special'];
6
9
  }): Type | 'unknown';
@@ -15,7 +15,6 @@ const roles_1 = require("../services/roles");
15
15
  const users_1 = require("../services/users");
16
16
  const merge_permissions_1 = require("../utils/merge-permissions");
17
17
  const merge_permissions_for_share_1 = require("./merge-permissions-for-share");
18
- const parse_json_1 = require("./parse-json");
19
18
  async function getPermissions(accountability, schema) {
20
19
  const database = (0, database_1.default)();
21
20
  const { systemCache, cache } = (0, cache_1.getCache)();
@@ -85,19 +84,19 @@ function parsePermissions(permissions) {
85
84
  permissions = permissions.map((permissionRaw) => {
86
85
  const permission = (0, lodash_1.cloneDeep)(permissionRaw);
87
86
  if (permission.permissions && typeof permission.permissions === 'string') {
88
- permission.permissions = (0, parse_json_1.parseJSON)(permission.permissions);
87
+ permission.permissions = (0, utils_1.parseJSON)(permission.permissions);
89
88
  }
90
89
  else if (permission.permissions === null) {
91
90
  permission.permissions = {};
92
91
  }
93
92
  if (permission.validation && typeof permission.validation === 'string') {
94
- permission.validation = (0, parse_json_1.parseJSON)(permission.validation);
93
+ permission.validation = (0, utils_1.parseJSON)(permission.validation);
95
94
  }
96
95
  else if (permission.validation === null) {
97
96
  permission.validation = {};
98
97
  }
99
98
  if (permission.presets && typeof permission.presets === 'string') {
100
- permission.presets = (0, parse_json_1.parseJSON)(permission.presets);
99
+ permission.presets = (0, utils_1.parseJSON)(permission.presets);
101
100
  }
102
101
  else if (permission.presets === null) {
103
102
  permission.presets = {};
@@ -17,7 +17,6 @@ const logger_1 = __importDefault(require("../logger"));
17
17
  const services_1 = require("../services");
18
18
  const get_default_value_1 = __importDefault(require("./get-default-value"));
19
19
  const get_local_type_1 = __importDefault(require("./get-local-type"));
20
- const parse_json_1 = require("./parse-json");
21
20
  async function getSchema(options) {
22
21
  const database = (options === null || options === void 0 ? void 0 : options.database) || (0, database_1.default)();
23
22
  const schemaInspector = (0, schema_1.default)(database);
@@ -120,7 +119,7 @@ async function getDatabaseSchema(database, schemaInspector) {
120
119
  const type = (existing && (0, get_local_type_1.default)(column, { special })) || 'alias';
121
120
  let validation = (_a = field.validation) !== null && _a !== void 0 ? _a : null;
122
121
  if (validation && typeof validation === 'string')
123
- validation = (0, parse_json_1.parseJSON)(validation);
122
+ validation = (0, utils_1.parseJSON)(validation);
124
123
  result.collections[field.collection].fields[field.field] = {
125
124
  field: field.field,
126
125
  defaultValue: (_b = existing === null || existing === void 0 ? void 0 : existing.defaultValue) !== null && _b !== void 0 ? _b : null,