@directus/api 11.0.1 → 12.0.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 (283) hide show
  1. package/dist/app.js +3 -4
  2. package/dist/auth/auth.d.ts +4 -4
  3. package/dist/auth/auth.js +2 -2
  4. package/dist/auth/drivers/ldap.js +20 -17
  5. package/dist/auth/drivers/local.js +5 -5
  6. package/dist/auth/drivers/oauth2.js +16 -16
  7. package/dist/auth/drivers/openid.js +18 -17
  8. package/dist/auth/drivers/saml.js +6 -7
  9. package/dist/auth.js +3 -2
  10. package/dist/cache.js +3 -13
  11. package/dist/cli/utils/create-env/env-stub.liquid +5 -7
  12. package/dist/controllers/activity.js +7 -6
  13. package/dist/controllers/assets.js +25 -12
  14. package/dist/controllers/auth.js +8 -7
  15. package/dist/controllers/collections.js +4 -3
  16. package/dist/controllers/dashboards.js +5 -4
  17. package/dist/controllers/extensions.js +3 -3
  18. package/dist/controllers/fields.js +9 -8
  19. package/dist/controllers/files.js +11 -11
  20. package/dist/controllers/flows.js +5 -4
  21. package/dist/controllers/folders.js +5 -4
  22. package/dist/controllers/items.js +14 -13
  23. package/dist/controllers/not-found.js +2 -2
  24. package/dist/controllers/notifications.js +5 -4
  25. package/dist/controllers/operations.js +5 -4
  26. package/dist/controllers/panels.js +5 -4
  27. package/dist/controllers/permissions.js +5 -4
  28. package/dist/controllers/presets.js +5 -4
  29. package/dist/controllers/relations.js +6 -5
  30. package/dist/controllers/roles.js +5 -4
  31. package/dist/controllers/schema.js +8 -8
  32. package/dist/controllers/server.js +2 -2
  33. package/dist/controllers/settings.js +3 -2
  34. package/dist/controllers/shares.js +7 -6
  35. package/dist/controllers/translations.js +6 -5
  36. package/dist/controllers/users.js +22 -21
  37. package/dist/controllers/utils.js +10 -10
  38. package/dist/controllers/webhooks.js +5 -4
  39. package/dist/{exceptions/database → database/errors}/dialects/mssql.js +8 -18
  40. package/dist/{exceptions/database → database/errors}/dialects/mysql.js +9 -19
  41. package/dist/{exceptions/database → database/errors}/dialects/oracle.js +2 -2
  42. package/dist/{exceptions/database → database/errors}/dialects/postgres.js +7 -18
  43. package/dist/{exceptions/database → database/errors}/dialects/sqlite.js +7 -10
  44. package/dist/{exceptions/database → database/errors}/translate.js +1 -1
  45. package/dist/database/migrations/run.js +10 -1
  46. package/dist/emitter.d.ts +3 -2
  47. package/dist/emitter.js +12 -4
  48. package/dist/env.js +21 -17
  49. package/dist/errors/codes.d.ts +29 -0
  50. package/dist/errors/codes.js +30 -0
  51. package/dist/errors/contains-null-values.d.ts +7 -0
  52. package/dist/errors/contains-null-values.js +4 -0
  53. package/dist/errors/content-too-large.d.ts +1 -0
  54. package/dist/errors/content-too-large.js +3 -0
  55. package/dist/errors/forbidden.d.ts +1 -0
  56. package/dist/errors/forbidden.js +3 -0
  57. package/dist/errors/hit-rate-limit.d.ts +6 -0
  58. package/dist/errors/hit-rate-limit.js +8 -0
  59. package/dist/errors/illegal-asset-transformation.d.ts +4 -0
  60. package/dist/errors/illegal-asset-transformation.js +3 -0
  61. package/dist/errors/index.d.ts +28 -0
  62. package/dist/errors/index.js +28 -0
  63. package/dist/errors/invalid-credentials.d.ts +1 -0
  64. package/dist/errors/invalid-credentials.js +3 -0
  65. package/dist/errors/invalid-foreign-key.d.ts +6 -0
  66. package/dist/errors/invalid-foreign-key.js +14 -0
  67. package/dist/errors/invalid-ip.d.ts +1 -0
  68. package/dist/errors/invalid-ip.js +3 -0
  69. package/dist/errors/invalid-otp.d.ts +1 -0
  70. package/dist/errors/invalid-otp.js +3 -0
  71. package/dist/errors/invalid-payload.d.ts +5 -0
  72. package/dist/errors/invalid-payload.js +4 -0
  73. package/dist/errors/invalid-provider-config.d.ts +5 -0
  74. package/dist/errors/invalid-provider-config.js +3 -0
  75. package/dist/errors/invalid-provider.d.ts +1 -0
  76. package/dist/errors/invalid-provider.js +3 -0
  77. package/dist/errors/invalid-query.d.ts +5 -0
  78. package/dist/errors/invalid-query.js +4 -0
  79. package/dist/errors/invalid-token.d.ts +1 -0
  80. package/dist/errors/invalid-token.js +3 -0
  81. package/dist/errors/method-not-allowed.d.ts +6 -0
  82. package/dist/errors/method-not-allowed.js +6 -0
  83. package/dist/errors/not-null-violation.d.ts +6 -0
  84. package/dist/errors/not-null-violation.js +14 -0
  85. package/dist/errors/range-not-satisfiable.d.ts +7 -0
  86. package/dist/errors/range-not-satisfiable.js +7 -0
  87. package/dist/errors/record-not-unique.d.ts +6 -0
  88. package/dist/errors/record-not-unique.js +14 -0
  89. package/dist/errors/route-not-found.d.ts +5 -0
  90. package/dist/errors/route-not-found.js +4 -0
  91. package/dist/errors/service-unavailable.d.ts +7 -0
  92. package/dist/errors/service-unavailable.js +4 -0
  93. package/dist/errors/token-expired.d.ts +1 -0
  94. package/dist/errors/token-expired.js +3 -0
  95. package/dist/errors/unexpected-response.d.ts +1 -0
  96. package/dist/errors/unexpected-response.js +3 -0
  97. package/dist/errors/unprocessable-content.d.ts +5 -0
  98. package/dist/errors/unprocessable-content.js +4 -0
  99. package/dist/errors/unsupported-media-type.d.ts +6 -0
  100. package/dist/errors/unsupported-media-type.js +4 -0
  101. package/dist/errors/user-suspended.d.ts +1 -0
  102. package/dist/errors/user-suspended.js +3 -0
  103. package/dist/errors/value-out-of-range.d.ts +6 -0
  104. package/dist/errors/value-out-of-range.js +14 -0
  105. package/dist/errors/value-too-long.d.ts +6 -0
  106. package/dist/errors/value-too-long.js +14 -0
  107. package/dist/extensions.js +0 -4
  108. package/dist/flows.js +6 -8
  109. package/dist/index.d.ts +0 -2
  110. package/dist/index.js +0 -2
  111. package/dist/messenger.d.ts +3 -3
  112. package/dist/messenger.js +18 -9
  113. package/dist/middleware/authenticate.js +2 -38
  114. package/dist/middleware/check-ip.js +2 -2
  115. package/dist/middleware/collection-exists.js +2 -2
  116. package/dist/middleware/error-handler.js +7 -7
  117. package/dist/middleware/graphql.js +11 -9
  118. package/dist/middleware/rate-limiter-global.d.ts +2 -2
  119. package/dist/middleware/rate-limiter-global.js +2 -3
  120. package/dist/middleware/rate-limiter-ip.d.ts +2 -2
  121. package/dist/middleware/rate-limiter-ip.js +2 -3
  122. package/dist/middleware/validate-batch.js +3 -4
  123. package/dist/rate-limiter.js +2 -9
  124. package/dist/server.js +10 -0
  125. package/dist/services/activity.js +3 -2
  126. package/dist/services/assets.js +9 -10
  127. package/dist/services/authentication.js +12 -11
  128. package/dist/services/authorization.d.ts +1 -1
  129. package/dist/services/authorization.js +16 -16
  130. package/dist/services/collections.js +17 -16
  131. package/dist/services/fields.js +16 -14
  132. package/dist/services/files.js +7 -6
  133. package/dist/services/graphql/errors/execution.d.ts +6 -0
  134. package/dist/services/graphql/errors/execution.js +2 -0
  135. package/dist/services/graphql/errors/index.d.ts +2 -0
  136. package/dist/services/graphql/errors/index.js +2 -0
  137. package/dist/services/graphql/errors/validation.d.ts +6 -0
  138. package/dist/services/graphql/errors/validation.js +2 -0
  139. package/dist/services/graphql/index.d.ts +2 -8
  140. package/dist/services/graphql/index.js +125 -66
  141. package/dist/services/graphql/subscription.d.ts +16 -0
  142. package/dist/services/graphql/subscription.js +77 -0
  143. package/dist/services/graphql/utils/process-error.js +3 -3
  144. package/dist/services/import-export.js +7 -7
  145. package/dist/services/index.d.ts +1 -0
  146. package/dist/services/index.js +1 -0
  147. package/dist/services/items.js +14 -13
  148. package/dist/services/mail/index.js +3 -3
  149. package/dist/services/meta.js +3 -3
  150. package/dist/services/payload.js +11 -7
  151. package/dist/services/relations.js +32 -22
  152. package/dist/services/revisions.js +3 -3
  153. package/dist/services/roles.js +10 -9
  154. package/dist/services/schema.js +5 -5
  155. package/dist/services/server.js +24 -0
  156. package/dist/services/shares.js +4 -4
  157. package/dist/services/tfa.js +6 -6
  158. package/dist/services/translations.d.ts +2 -2
  159. package/dist/services/translations.js +4 -4
  160. package/dist/services/users.js +26 -29
  161. package/dist/services/utils.js +4 -4
  162. package/dist/services/websocket.d.ts +14 -0
  163. package/dist/services/websocket.js +26 -0
  164. package/dist/synchronization.js +3 -3
  165. package/dist/types/items.d.ts +2 -2
  166. package/dist/utils/apply-diff.js +13 -4
  167. package/dist/utils/apply-query.js +22 -13
  168. package/dist/utils/get-accountability-for-role.js +1 -2
  169. package/dist/utils/get-accountability-for-token.d.ts +2 -0
  170. package/dist/utils/get-accountability-for-token.js +50 -0
  171. package/dist/utils/get-column-path.js +5 -3
  172. package/dist/utils/get-column.js +3 -3
  173. package/dist/utils/get-service.d.ts +7 -0
  174. package/dist/utils/get-service.js +49 -0
  175. package/dist/utils/jwt.js +5 -5
  176. package/dist/utils/redact.d.ts +4 -0
  177. package/dist/utils/redact.js +15 -1
  178. package/dist/utils/to-boolean.d.ts +4 -0
  179. package/dist/utils/to-boolean.js +6 -0
  180. package/dist/utils/validate-diff.js +23 -9
  181. package/dist/utils/validate-keys.js +3 -3
  182. package/dist/utils/validate-query.d.ts +2 -0
  183. package/dist/utils/validate-query.js +27 -21
  184. package/dist/utils/validate-snapshot.js +11 -5
  185. package/dist/websocket/authenticate.d.ts +6 -0
  186. package/dist/websocket/authenticate.js +59 -0
  187. package/dist/websocket/controllers/base.d.ts +42 -0
  188. package/dist/websocket/controllers/base.js +279 -0
  189. package/dist/websocket/controllers/graphql.d.ts +12 -0
  190. package/dist/websocket/controllers/graphql.js +102 -0
  191. package/dist/websocket/controllers/hooks.d.ts +1 -0
  192. package/dist/websocket/controllers/hooks.js +122 -0
  193. package/dist/websocket/controllers/index.d.ts +10 -0
  194. package/dist/websocket/controllers/index.js +31 -0
  195. package/dist/websocket/controllers/rest.d.ts +9 -0
  196. package/dist/websocket/controllers/rest.js +47 -0
  197. package/dist/websocket/errors.d.ts +16 -0
  198. package/dist/websocket/errors.js +55 -0
  199. package/dist/websocket/handlers/heartbeat.d.ts +11 -0
  200. package/dist/websocket/handlers/heartbeat.js +72 -0
  201. package/dist/websocket/handlers/index.d.ts +4 -0
  202. package/dist/websocket/handlers/index.js +11 -0
  203. package/dist/websocket/handlers/items.d.ts +6 -0
  204. package/dist/websocket/handlers/items.js +103 -0
  205. package/dist/websocket/handlers/subscribe.d.ts +43 -0
  206. package/dist/websocket/handlers/subscribe.js +278 -0
  207. package/dist/websocket/messages.d.ts +311 -0
  208. package/dist/websocket/messages.js +96 -0
  209. package/dist/websocket/types.d.ts +34 -0
  210. package/dist/websocket/types.js +1 -0
  211. package/dist/websocket/utils/get-expires-at-for-token.d.ts +1 -0
  212. package/dist/websocket/utils/get-expires-at-for-token.js +8 -0
  213. package/dist/websocket/utils/message.d.ts +4 -0
  214. package/dist/websocket/utils/message.js +27 -0
  215. package/dist/websocket/utils/wait-for-message.d.ts +4 -0
  216. package/dist/websocket/utils/wait-for-message.js +45 -0
  217. package/package.json +21 -16
  218. package/dist/exceptions/content-too-large.d.ts +0 -4
  219. package/dist/exceptions/content-too-large.js +0 -6
  220. package/dist/exceptions/database/contains-null-values.d.ts +0 -9
  221. package/dist/exceptions/database/contains-null-values.js +0 -6
  222. package/dist/exceptions/database/invalid-foreign-key.d.ts +0 -10
  223. package/dist/exceptions/database/invalid-foreign-key.js +0 -11
  224. package/dist/exceptions/database/not-null-violation.d.ts +0 -9
  225. package/dist/exceptions/database/not-null-violation.js +0 -6
  226. package/dist/exceptions/database/record-not-unique.d.ts +0 -10
  227. package/dist/exceptions/database/record-not-unique.js +0 -11
  228. package/dist/exceptions/database/value-out-of-range.d.ts +0 -10
  229. package/dist/exceptions/database/value-out-of-range.js +0 -11
  230. package/dist/exceptions/database/value-too-long.d.ts +0 -9
  231. package/dist/exceptions/database/value-too-long.js +0 -11
  232. package/dist/exceptions/forbidden.d.ts +0 -6
  233. package/dist/exceptions/forbidden.js +0 -13
  234. package/dist/exceptions/graphql-validation.d.ts +0 -4
  235. package/dist/exceptions/graphql-validation.js +0 -6
  236. package/dist/exceptions/hit-rate-limit.d.ts +0 -9
  237. package/dist/exceptions/hit-rate-limit.js +0 -6
  238. package/dist/exceptions/illegal-asset-transformation.d.ts +0 -4
  239. package/dist/exceptions/illegal-asset-transformation.js +0 -6
  240. package/dist/exceptions/index.d.ts +0 -21
  241. package/dist/exceptions/index.js +0 -21
  242. package/dist/exceptions/invalid-config.d.ts +0 -4
  243. package/dist/exceptions/invalid-config.js +0 -6
  244. package/dist/exceptions/invalid-credentials.d.ts +0 -4
  245. package/dist/exceptions/invalid-credentials.js +0 -6
  246. package/dist/exceptions/invalid-ip.d.ts +0 -4
  247. package/dist/exceptions/invalid-ip.js +0 -6
  248. package/dist/exceptions/invalid-otp.d.ts +0 -4
  249. package/dist/exceptions/invalid-otp.js +0 -6
  250. package/dist/exceptions/invalid-payload.d.ts +0 -4
  251. package/dist/exceptions/invalid-payload.js +0 -6
  252. package/dist/exceptions/invalid-provider.d.ts +0 -4
  253. package/dist/exceptions/invalid-provider.js +0 -6
  254. package/dist/exceptions/invalid-query.d.ts +0 -4
  255. package/dist/exceptions/invalid-query.js +0 -6
  256. package/dist/exceptions/invalid-token.d.ts +0 -4
  257. package/dist/exceptions/invalid-token.js +0 -6
  258. package/dist/exceptions/method-not-allowed.d.ts +0 -8
  259. package/dist/exceptions/method-not-allowed.js +0 -6
  260. package/dist/exceptions/range-not-satisfiable.d.ts +0 -5
  261. package/dist/exceptions/range-not-satisfiable.js +0 -9
  262. package/dist/exceptions/route-not-found.d.ts +0 -4
  263. package/dist/exceptions/route-not-found.js +0 -6
  264. package/dist/exceptions/service-unavailable.d.ts +0 -9
  265. package/dist/exceptions/service-unavailable.js +0 -6
  266. package/dist/exceptions/token-expired.d.ts +0 -4
  267. package/dist/exceptions/token-expired.js +0 -6
  268. package/dist/exceptions/unexpected-response.d.ts +0 -4
  269. package/dist/exceptions/unexpected-response.js +0 -6
  270. package/dist/exceptions/unprocessable-entity.d.ts +0 -4
  271. package/dist/exceptions/unprocessable-entity.js +0 -6
  272. package/dist/exceptions/unsupported-media-type.d.ts +0 -4
  273. package/dist/exceptions/unsupported-media-type.js +0 -6
  274. package/dist/exceptions/user-suspended.d.ts +0 -4
  275. package/dist/exceptions/user-suspended.js +0 -6
  276. /package/dist/{exceptions/database → database/errors}/dialects/mssql.d.ts +0 -0
  277. /package/dist/{exceptions/database → database/errors}/dialects/mysql.d.ts +0 -0
  278. /package/dist/{exceptions/database → database/errors}/dialects/oracle.d.ts +0 -0
  279. /package/dist/{exceptions/database → database/errors}/dialects/postgres.d.ts +0 -0
  280. /package/dist/{exceptions/database → database/errors}/dialects/sqlite.d.ts +0 -0
  281. /package/dist/{exceptions/database → database/errors}/dialects/types.d.ts +0 -0
  282. /package/dist/{exceptions/database → database/errors}/dialects/types.js +0 -0
  283. /package/dist/{exceptions/database → database/errors}/translate.d.ts +0 -0
@@ -8,7 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
8
8
  import { systemCollectionRows } from '../database/system-data/collections/index.js';
9
9
  import emitter from '../emitter.js';
10
10
  import env from '../env.js';
11
- import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
11
+ import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
12
12
  import { FieldsService } from '../services/fields.js';
13
13
  import { ItemsService } from '../services/items.js';
14
14
  import { getSchema } from '../utils/get-schema.js';
@@ -36,12 +36,12 @@ export class CollectionsService {
36
36
  */
37
37
  async createOne(payload, opts) {
38
38
  if (this.accountability && this.accountability.admin !== true) {
39
- throw new ForbiddenException();
39
+ throw new ForbiddenError();
40
40
  }
41
41
  if (!payload.collection)
42
- throw new InvalidPayloadException(`"collection" is required`);
42
+ throw new InvalidPayloadError({ reason: `"collection" is required` });
43
43
  if (payload.collection.startsWith('directus_')) {
44
- throw new InvalidPayloadException(`Collections can't start with "directus_"`);
44
+ throw new InvalidPayloadError({ reason: `Collections can't start with "directus_"` });
45
45
  }
46
46
  const nestedActionEvents = [];
47
47
  try {
@@ -51,7 +51,7 @@ export class CollectionsService {
51
51
  ...Object.keys(this.schema.collections),
52
52
  ];
53
53
  if (existingCollections.includes(payload.collection)) {
54
- throw new InvalidPayloadException(`Collection "${payload.collection}" already exists.`);
54
+ throw new InvalidPayloadError({ reason: `Collection "${payload.collection}" already exists` });
55
55
  }
56
56
  // Create the collection/fields in a transaction so it'll be reverted in case of errors or
57
57
  // permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
@@ -255,7 +255,7 @@ export class CollectionsService {
255
255
  async readOne(collectionKey) {
256
256
  const result = await this.readMany([collectionKey]);
257
257
  if (result.length === 0)
258
- throw new ForbiddenException();
258
+ throw new ForbiddenError();
259
259
  return result[0];
260
260
  }
261
261
  /**
@@ -270,7 +270,7 @@ export class CollectionsService {
270
270
  const collectionsYouHavePermissionToRead = permissions.map(({ collection }) => collection);
271
271
  for (const collectionKey of collectionKeys) {
272
272
  if (collectionsYouHavePermissionToRead.includes(collectionKey) === false) {
273
- throw new ForbiddenException();
273
+ throw new ForbiddenError();
274
274
  }
275
275
  }
276
276
  }
@@ -283,7 +283,7 @@ export class CollectionsService {
283
283
  */
284
284
  async updateOne(collectionKey, data, opts) {
285
285
  if (this.accountability && this.accountability.admin !== true) {
286
- throw new ForbiddenException();
286
+ throw new ForbiddenError();
287
287
  }
288
288
  const nestedActionEvents = [];
289
289
  try {
@@ -336,10 +336,10 @@ export class CollectionsService {
336
336
  */
337
337
  async updateBatch(data, opts) {
338
338
  if (this.accountability && this.accountability.admin !== true) {
339
- throw new ForbiddenException();
339
+ throw new ForbiddenError();
340
340
  }
341
341
  if (!Array.isArray(data)) {
342
- throw new InvalidPayloadException('Input should be an array of collection changes.');
342
+ throw new InvalidPayloadError({ reason: 'Input should be an array of collection changes' });
343
343
  }
344
344
  const collectionKey = 'collection';
345
345
  const collectionKeys = [];
@@ -352,8 +352,9 @@ export class CollectionsService {
352
352
  schema: this.schema,
353
353
  });
354
354
  for (const payload of data) {
355
- if (!payload[collectionKey])
356
- throw new InvalidPayloadException(`Collection in update misses collection key.`);
355
+ if (!payload[collectionKey]) {
356
+ throw new InvalidPayloadError({ reason: `Collection in update misses collection key` });
357
+ }
357
358
  await collectionItemsService.updateOne(payload[collectionKey], omit(payload, collectionKey), {
358
359
  autoPurgeCache: false,
359
360
  autoPurgeSystemCache: false,
@@ -385,7 +386,7 @@ export class CollectionsService {
385
386
  */
386
387
  async updateMany(collectionKeys, data, opts) {
387
388
  if (this.accountability && this.accountability.admin !== true) {
388
- throw new ForbiddenException();
389
+ throw new ForbiddenError();
389
390
  }
390
391
  const nestedActionEvents = [];
391
392
  try {
@@ -427,14 +428,14 @@ export class CollectionsService {
427
428
  */
428
429
  async deleteOne(collectionKey, opts) {
429
430
  if (this.accountability && this.accountability.admin !== true) {
430
- throw new ForbiddenException();
431
+ throw new ForbiddenError();
431
432
  }
432
433
  const nestedActionEvents = [];
433
434
  try {
434
435
  const collections = await this.readByQuery();
435
436
  const collectionToBeDeleted = collections.find((collection) => collection.collection === collectionKey);
436
437
  if (!!collectionToBeDeleted === false) {
437
- throw new ForbiddenException();
438
+ throw new ForbiddenError();
438
439
  }
439
440
  await this.knex.transaction(async (trx) => {
440
441
  if (collectionToBeDeleted.schema) {
@@ -531,7 +532,7 @@ export class CollectionsService {
531
532
  */
532
533
  async deleteMany(collectionKeys, opts) {
533
534
  if (this.accountability && this.accountability.admin !== true) {
534
- throw new ForbiddenException();
535
+ throw new ForbiddenError();
535
536
  }
536
537
  const nestedActionEvents = [];
537
538
  try {
@@ -4,12 +4,12 @@ import { addFieldFlag, toArray } from '@directus/utils';
4
4
  import { isEqual, isNil } from 'lodash-es';
5
5
  import { clearSystemCache, getCache } from '../cache.js';
6
6
  import { ALIAS_TYPES } from '../constants.js';
7
+ import { translateDatabaseError } from '../database/errors/translate.js';
7
8
  import { getHelpers } from '../database/helpers/index.js';
8
9
  import getDatabase, { getSchemaInspector } from '../database/index.js';
9
10
  import { systemFieldRows } from '../database/system-data/fields/index.js';
10
11
  import emitter from '../emitter.js';
11
- import { translateDatabaseError } from '../exceptions/database/translate.js';
12
- import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
12
+ import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
13
13
  import { ItemsService } from '../services/items.js';
14
14
  import { PayloadService } from '../services/payload.js';
15
15
  import getDefaultValue from '../utils/get-default-value.js';
@@ -48,7 +48,7 @@ export class FieldsService {
48
48
  async readAll(collection) {
49
49
  let fields;
50
50
  if (this.accountability && this.accountability.admin !== true && this.hasReadAccess === false) {
51
- throw new ForbiddenException();
51
+ throw new ForbiddenError();
52
52
  }
53
53
  const nonAuthorizedItemsService = new ItemsService('directus_fields', {
54
54
  knex: this.knex,
@@ -125,7 +125,7 @@ export class FieldsService {
125
125
  allowedFieldsInCollection[permission.collection] = permission.fields ?? [];
126
126
  });
127
127
  if (collection && collection in allowedFieldsInCollection === false) {
128
- throw new ForbiddenException();
128
+ throw new ForbiddenError();
129
129
  }
130
130
  return result.filter((field) => {
131
131
  if (field.collection in allowedFieldsInCollection === false)
@@ -151,17 +151,17 @@ export class FieldsService {
151
151
  async readOne(collection, field) {
152
152
  if (this.accountability && this.accountability.admin !== true) {
153
153
  if (this.hasReadAccess === false) {
154
- throw new ForbiddenException();
154
+ throw new ForbiddenError();
155
155
  }
156
156
  const permissions = this.accountability.permissions.find((permission) => {
157
157
  return permission.action === 'read' && permission.collection === collection;
158
158
  });
159
159
  if (!permissions || !permissions.fields)
160
- throw new ForbiddenException();
160
+ throw new ForbiddenError();
161
161
  if (permissions.fields.includes('*') === false) {
162
162
  const allowedFields = permissions.fields;
163
163
  if (allowedFields.includes(field) === false)
164
- throw new ForbiddenException();
164
+ throw new ForbiddenError();
165
165
  }
166
166
  }
167
167
  let column = undefined;
@@ -179,7 +179,7 @@ export class FieldsService {
179
179
  // Do nothing
180
180
  }
181
181
  if (!column && !fieldInfo)
182
- throw new ForbiddenException();
182
+ throw new ForbiddenError();
183
183
  const type = getLocalType(column, fieldInfo);
184
184
  const columnWithCastDefaultValue = column
185
185
  ? {
@@ -199,7 +199,7 @@ export class FieldsService {
199
199
  async createField(collection, field, table, // allows collection creation to
200
200
  opts) {
201
201
  if (this.accountability && this.accountability.admin !== true) {
202
- throw new ForbiddenException();
202
+ throw new ForbiddenError();
203
203
  }
204
204
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
205
205
  const nestedActionEvents = [];
@@ -208,7 +208,9 @@ export class FieldsService {
208
208
  isNil(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
209
209
  // Check if field already exists, either as a column, or as a row in directus_fields
210
210
  if (exists) {
211
- throw new InvalidPayloadException(`Field "${field.field}" already exists in collection "${collection}"`);
211
+ throw new InvalidPayloadError({
212
+ reason: `Field "${field.field}" already exists in collection "${collection}"`,
213
+ });
212
214
  }
213
215
  // Add flag for specific database type overrides
214
216
  const flagToAdd = this.helpers.date.fieldFlagForField(field.type);
@@ -287,7 +289,7 @@ export class FieldsService {
287
289
  }
288
290
  async updateField(collection, field, opts) {
289
291
  if (this.accountability && this.accountability.admin !== true) {
290
- throw new ForbiddenException();
292
+ throw new ForbiddenError();
291
293
  }
292
294
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
293
295
  const nestedActionEvents = [];
@@ -307,7 +309,7 @@ export class FieldsService {
307
309
  (hookAdjustedField.type === 'alias' ||
308
310
  this.schema.collections[collection].fields[field.field]?.type === 'alias') &&
309
311
  hookAdjustedField.type !== (this.schema.collections[collection].fields[field.field]?.type ?? 'alias')) {
310
- throw new InvalidPayloadException('Alias type cannot be changed');
312
+ throw new InvalidPayloadError({ reason: 'Alias type cannot be changed' });
311
313
  }
312
314
  if (hookAdjustedField.schema) {
313
315
  const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
@@ -384,7 +386,7 @@ export class FieldsService {
384
386
  }
385
387
  async deleteField(collection, field, opts) {
386
388
  if (this.accountability && this.accountability.admin !== true) {
387
- throw new ForbiddenException();
389
+ throw new ForbiddenError();
388
390
  }
389
391
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
390
392
  const nestedActionEvents = [];
@@ -551,7 +553,7 @@ export class FieldsService {
551
553
  column = table[field.type](field.field);
552
554
  }
553
555
  else {
554
- throw new InvalidPayloadException(`Illegal type passed: "${field.type}"`);
556
+ throw new InvalidPayloadError({ reason: `Illegal type passed: "${field.type}"` });
555
557
  }
556
558
  if (field.schema?.default_value !== undefined) {
557
559
  if (typeof field.schema.default_value === 'string' &&
@@ -12,7 +12,7 @@ import url from 'url';
12
12
  import { SUPPORTED_IMAGE_METADATA_FORMATS } from '../constants.js';
13
13
  import emitter from '../emitter.js';
14
14
  import env from '../env.js';
15
- import { ForbiddenException, InvalidPayloadException, ServiceUnavailableException } from '../exceptions/index.js';
15
+ import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError } from '../errors/index.js';
16
16
  import logger from '../logger.js';
17
17
  import { getAxios } from '../request/index.js';
18
18
  import { getStorage } from '../storage/index.js';
@@ -67,7 +67,7 @@ export class FilesService extends ItemsService {
67
67
  logger.warn(`Couldn't save file ${payload.filename_disk}`);
68
68
  logger.warn(err);
69
69
  await this.deleteOne(primaryKey);
70
- throw new ServiceUnavailableException(`Couldn't save file ${payload.filename_disk}`, { service: 'files' });
70
+ throw new ServiceUnavailableError({ service: 'files', reason: `Couldn't save file ${payload.filename_disk}` });
71
71
  }
72
72
  const { size } = await storage.location(data.storage).stat(payload.filename_disk);
73
73
  payload.filesize = size;
@@ -202,7 +202,7 @@ export class FilesService extends ItemsService {
202
202
  async importOne(importURL, body) {
203
203
  const fileCreatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
204
204
  if (this.accountability && this.accountability?.admin !== true && !fileCreatePermissions) {
205
- throw new ForbiddenException();
205
+ throw new ForbiddenError();
206
206
  }
207
207
  let fileResponse;
208
208
  try {
@@ -213,8 +213,9 @@ export class FilesService extends ItemsService {
213
213
  }
214
214
  catch (err) {
215
215
  logger.warn(err, `Couldn't fetch file from URL "${importURL}"`);
216
- throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
216
+ throw new ServiceUnavailableError({
217
217
  service: 'external-file',
218
+ reason: `Couldn't fetch file from url "${importURL}"`,
218
219
  });
219
220
  }
220
221
  const parsedURL = url.parse(fileResponse.request.res.responseUrl);
@@ -234,7 +235,7 @@ export class FilesService extends ItemsService {
234
235
  */
235
236
  async createOne(data, opts) {
236
237
  if (!data.type) {
237
- throw new InvalidPayloadException(`"type" is required`);
238
+ throw new InvalidPayloadError({ reason: `"type" is required` });
238
239
  }
239
240
  const key = await super.createOne(data, opts);
240
241
  return key;
@@ -253,7 +254,7 @@ export class FilesService extends ItemsService {
253
254
  const storage = await getStorage();
254
255
  const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
255
256
  if (!files) {
256
- throw new ForbiddenException();
257
+ throw new ForbiddenError();
257
258
  }
258
259
  await super.deleteMany(keys);
259
260
  for (const file of files) {
@@ -0,0 +1,6 @@
1
+ import type { GraphQLError } from 'graphql';
2
+ interface GraphQLExecutionErrorExtensions {
3
+ errors: GraphQLError[];
4
+ }
5
+ export declare const GraphQLExecutionError: import("@directus/errors").DirectusErrorConstructor<GraphQLExecutionErrorExtensions>;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import { createError } from '@directus/errors';
2
+ export const GraphQLExecutionError = createError('GRAPHQL_EXECUTION', 'GraphQL execution error.', 400);
@@ -0,0 +1,2 @@
1
+ export { GraphQLExecutionError } from './execution.js';
2
+ export { GraphQLValidationError } from './validation.js';
@@ -0,0 +1,2 @@
1
+ export { GraphQLExecutionError } from './execution.js';
2
+ export { GraphQLValidationError } from './validation.js';
@@ -0,0 +1,6 @@
1
+ import type { GraphQLError } from 'graphql';
2
+ interface GraphQLValidationErrorExtensions {
3
+ errors: GraphQLError[];
4
+ }
5
+ export declare const GraphQLValidationError: import("@directus/errors").DirectusErrorConstructor<GraphQLValidationErrorExtensions>;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import { createError } from '@directus/errors';
2
+ export const GraphQLValidationError = createError('GRAPHQL_VALIDATION', 'GraphQL validation error.', 400);
@@ -1,11 +1,10 @@
1
- import type { BaseException } from '@directus/exceptions';
1
+ import type { DirectusError } from '@directus/errors';
2
2
  import type { Accountability, Filter, Query, SchemaOverview } from '@directus/types';
3
3
  import type { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
4
4
  import { GraphQLError, GraphQLSchema } from 'graphql';
5
5
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
6
6
  import type { Knex } from 'knex';
7
7
  import type { AbstractServiceOptions, GraphQLParams, Item } from '../../types/index.js';
8
- import { ItemsService } from '../items.js';
9
8
  export declare class GraphQLService {
10
9
  accountability: Accountability | null;
11
10
  knex: Knex;
@@ -62,12 +61,7 @@ export declare class GraphQLService {
62
61
  /**
63
62
  * Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
64
63
  */
65
- formatError(error: BaseException | BaseException[]): GraphQLError;
66
- /**
67
- * Select the correct service for the given collection. This allows the individual services to run
68
- * their custom checks (f.e. it allows UsersService to prevent updating TFA secret from outside)
69
- */
70
- getService(collection: string): ItemsService;
64
+ formatError(error: DirectusError | DirectusError[]): GraphQLError;
71
65
  /**
72
66
  * Replace all fragments in a selectionset for the actual selection set as defined in the fragment
73
67
  * Effectively merges the selections with the fragments used in those selections