directus 9.22.4 → 9.23.1

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 (112) hide show
  1. package/dist/app.js +5 -4
  2. package/dist/auth/drivers/ldap.d.ts +2 -2
  3. package/dist/auth/drivers/ldap.js +8 -8
  4. package/dist/auth/drivers/oauth2.js +2 -2
  5. package/dist/auth/drivers/openid.js +2 -2
  6. package/dist/cache.js +4 -4
  7. package/dist/cli/commands/schema/apply.js +19 -17
  8. package/dist/cli/utils/create-db-connection.d.ts +2 -1
  9. package/dist/cli/utils/create-env/env-stub.liquid +1 -1
  10. package/dist/cli/utils/drivers.d.ts +3 -9
  11. package/dist/constants.d.ts +2 -8
  12. package/dist/constants.js +3 -7
  13. package/dist/controllers/assets.js +5 -5
  14. package/dist/controllers/extensions.js +7 -7
  15. package/dist/controllers/files.js +1 -1
  16. package/dist/controllers/graphql.js +8 -0
  17. package/dist/controllers/schema.d.ts +2 -0
  18. package/dist/controllers/schema.js +98 -0
  19. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -1
  20. package/dist/database/helpers/schema/dialects/oracle.d.ts +4 -1
  21. package/dist/database/helpers/schema/dialects/oracle.js +25 -0
  22. package/dist/database/helpers/schema/types.d.ts +8 -6
  23. package/dist/database/helpers/schema/types.js +7 -1
  24. package/dist/database/index.d.ts +2 -1
  25. package/dist/database/run-ast.js +2 -2
  26. package/dist/env.js +9 -2
  27. package/dist/extensions.js +1 -1
  28. package/dist/flows.js +17 -8
  29. package/dist/middleware/cache.js +2 -2
  30. package/dist/middleware/respond.js +14 -9
  31. package/dist/operations/request/index.js +2 -1
  32. package/dist/operations/trigger/index.d.ts +2 -0
  33. package/dist/operations/trigger/index.js +26 -9
  34. package/dist/request/index.d.ts +5 -0
  35. package/dist/request/index.js +18 -0
  36. package/dist/request/index.test.d.ts +1 -0
  37. package/dist/request/request-interceptor.d.ts +2 -0
  38. package/dist/request/request-interceptor.js +33 -0
  39. package/dist/request/request-interceptor.test.d.ts +1 -0
  40. package/dist/request/response-interceptor.d.ts +2 -0
  41. package/dist/request/response-interceptor.js +9 -0
  42. package/dist/request/response-interceptor.test.d.ts +1 -0
  43. package/dist/request/validate-ip.d.ts +1 -0
  44. package/dist/request/validate-ip.js +27 -0
  45. package/dist/request/validate-ip.test.d.ts +1 -0
  46. package/dist/services/assets.d.ts +1 -1
  47. package/dist/services/assets.js +11 -2
  48. package/dist/services/authentication.js +5 -5
  49. package/dist/services/fields.js +1 -0
  50. package/dist/services/files.js +44 -88
  51. package/dist/services/graphql/index.js +14 -8
  52. package/dist/services/graphql/utils/process-error.js +22 -9
  53. package/dist/services/import-export.d.ts +4 -2
  54. package/dist/services/import-export.js +17 -3
  55. package/dist/services/import-export.test.d.ts +1 -0
  56. package/dist/services/index.d.ts +1 -0
  57. package/dist/services/index.js +1 -0
  58. package/dist/services/items.js +34 -15
  59. package/dist/services/relations.js +2 -0
  60. package/dist/services/roles.js +32 -11
  61. package/dist/services/schema.d.ts +15 -0
  62. package/dist/services/schema.js +58 -0
  63. package/dist/services/schema.test.d.ts +1 -0
  64. package/dist/services/shares.d.ts +2 -2
  65. package/dist/services/shares.js +9 -9
  66. package/dist/services/users.js +74 -47
  67. package/dist/types/assets.d.ts +1 -1
  68. package/dist/types/database.d.ts +3 -0
  69. package/dist/types/database.js +4 -0
  70. package/dist/types/index.d.ts +1 -0
  71. package/dist/types/index.js +1 -0
  72. package/dist/types/items.d.ts +5 -0
  73. package/dist/types/snapshot.d.ts +22 -0
  74. package/dist/types/snapshot.js +14 -0
  75. package/dist/utils/apply-diff.d.ts +9 -0
  76. package/dist/utils/apply-diff.js +259 -0
  77. package/dist/utils/apply-diff.test.d.ts +1 -0
  78. package/dist/utils/apply-query.js +8 -6
  79. package/dist/utils/apply-snapshot.d.ts +1 -3
  80. package/dist/utils/apply-snapshot.js +4 -234
  81. package/dist/utils/get-cache-headers.d.ts +3 -1
  82. package/dist/utils/get-cache-headers.js +20 -19
  83. package/dist/utils/get-cache-headers.test.d.ts +1 -0
  84. package/dist/utils/get-milliseconds.d.ts +4 -0
  85. package/dist/utils/get-milliseconds.js +15 -0
  86. package/dist/utils/get-milliseconds.test.d.ts +1 -0
  87. package/dist/utils/get-snapshot-diff.js +11 -7
  88. package/dist/utils/get-snapshot.js +29 -6
  89. package/dist/utils/get-versioned-hash.d.ts +1 -0
  90. package/dist/utils/get-versioned-hash.js +12 -0
  91. package/dist/utils/get-versioned-hash.test.d.ts +1 -0
  92. package/dist/utils/map-values-deep.d.ts +1 -0
  93. package/dist/utils/map-values-deep.js +29 -0
  94. package/dist/utils/map-values-deep.test.d.ts +1 -0
  95. package/dist/utils/sanitize-schema.d.ts +30 -0
  96. package/dist/utils/sanitize-schema.js +80 -0
  97. package/dist/utils/sanitize-schema.test.d.ts +1 -0
  98. package/dist/utils/track.js +3 -3
  99. package/dist/utils/url.js +2 -6
  100. package/dist/utils/url.test.d.ts +1 -0
  101. package/dist/utils/validate-diff.d.ts +7 -0
  102. package/dist/utils/validate-diff.js +114 -0
  103. package/dist/utils/validate-diff.test.d.ts +1 -0
  104. package/dist/utils/validate-query.js +1 -1
  105. package/dist/utils/validate-query.test.d.ts +1 -0
  106. package/dist/utils/validate-snapshot.d.ts +5 -0
  107. package/dist/utils/validate-snapshot.js +71 -0
  108. package/dist/utils/validate-snapshot.test.d.ts +1 -0
  109. package/dist/utils/with-timeout.d.ts +1 -0
  110. package/dist/utils/with-timeout.js +16 -0
  111. package/dist/webhooks.js +3 -2
  112. package/package.json +54 -53
@@ -1,56 +1,29 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = 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);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.FilesService = void 0;
30
7
  const utils_1 = require("@directus/shared/utils");
31
- const dns_1 = require("dns");
32
8
  const encodeurl_1 = __importDefault(require("encodeurl"));
33
9
  const exif_reader_1 = __importDefault(require("exif-reader"));
34
10
  const icc_1 = require("icc");
35
11
  const lodash_1 = require("lodash");
36
12
  const mime_types_1 = require("mime-types");
37
- const net_1 = __importDefault(require("net"));
38
13
  const promises_1 = require("node:stream/promises");
39
- const os_1 = __importDefault(require("os"));
40
14
  const path_1 = __importDefault(require("path"));
41
15
  const sharp_1 = __importDefault(require("sharp"));
42
- const url_1 = __importStar(require("url"));
43
- const util_1 = require("util");
16
+ const url_1 = __importDefault(require("url"));
44
17
  const emitter_1 = __importDefault(require("../emitter"));
45
18
  const env_1 = __importDefault(require("../env"));
46
19
  const exceptions_1 = require("../exceptions");
47
20
  const logger_1 = __importDefault(require("../logger"));
21
+ const index_1 = require("../request/index");
48
22
  const storage_1 = require("../storage");
49
23
  const parse_image_metadata_1 = require("../utils/parse-image-metadata");
50
24
  const items_1 = require("./items");
51
25
  // @ts-ignore
52
26
  const format_title_1 = __importDefault(require("@directus/format-title"));
53
- const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
54
27
  class FilesService extends items_1.ItemsService {
55
28
  constructor(options) {
56
29
  super('directus_files', options);
@@ -112,7 +85,7 @@ class FilesService extends items_1.ItemsService {
112
85
  schema: this.schema,
113
86
  });
114
87
  await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
115
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
88
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
116
89
  await this.cache.clear();
117
90
  }
118
91
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
@@ -135,8 +108,10 @@ class FilesService extends items_1.ItemsService {
135
108
  return new Promise((resolve, reject) => {
136
109
  (0, promises_1.pipeline)(stream, (0, sharp_1.default)().metadata(async (err, sharpMetadata) => {
137
110
  var _a, _b, _c, _d;
138
- if (err)
111
+ if (err) {
139
112
  reject(err);
113
+ return;
114
+ }
140
115
  const metadata = {};
141
116
  if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
142
117
  metadata.height = sharpMetadata.width;
@@ -149,26 +124,50 @@ class FilesService extends items_1.ItemsService {
149
124
  // Backward-compatible layout as it used to be with 'exifr'
150
125
  const fullMetadata = {};
151
126
  if (sharpMetadata.exif) {
152
- const { image, thumbnail, interoperability, ...rest } = (0, exif_reader_1.default)(sharpMetadata.exif);
153
- if (image) {
154
- fullMetadata.ifd0 = image;
155
- }
156
- if (thumbnail) {
157
- fullMetadata.ifd1 = thumbnail;
127
+ try {
128
+ const { image, thumbnail, interoperability, ...rest } = (0, exif_reader_1.default)(sharpMetadata.exif);
129
+ if (image) {
130
+ fullMetadata.ifd0 = image;
131
+ }
132
+ if (thumbnail) {
133
+ fullMetadata.ifd1 = thumbnail;
134
+ }
135
+ if (interoperability) {
136
+ fullMetadata.interop = interoperability;
137
+ }
138
+ Object.assign(fullMetadata, rest);
158
139
  }
159
- if (interoperability) {
160
- fullMetadata.interop = interoperability;
140
+ catch (err) {
141
+ logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
142
+ logger_1.default.warn(err);
161
143
  }
162
- Object.assign(fullMetadata, rest);
163
144
  }
164
145
  if (sharpMetadata.icc) {
165
- fullMetadata.icc = (0, icc_1.parse)(sharpMetadata.icc);
146
+ try {
147
+ fullMetadata.icc = (0, icc_1.parse)(sharpMetadata.icc);
148
+ }
149
+ catch (err) {
150
+ logger_1.default.warn(`Couldn't extract ICC profile data from file`);
151
+ logger_1.default.warn(err);
152
+ }
166
153
  }
167
154
  if (sharpMetadata.iptc) {
168
- fullMetadata.iptc = (0, parse_image_metadata_1.parseIptc)(sharpMetadata.iptc);
155
+ try {
156
+ fullMetadata.iptc = (0, parse_image_metadata_1.parseIptc)(sharpMetadata.iptc);
157
+ }
158
+ catch (err) {
159
+ logger_1.default.warn(`Couldn't extract IPTC Photo Metadata from file`);
160
+ logger_1.default.warn(err);
161
+ }
169
162
  }
170
163
  if (sharpMetadata.xmp) {
171
- fullMetadata.xmp = (0, parse_image_metadata_1.parseXmp)(sharpMetadata.xmp);
164
+ try {
165
+ fullMetadata.xmp = (0, parse_image_metadata_1.parseXmp)(sharpMetadata.xmp);
166
+ }
167
+ catch (err) {
168
+ logger_1.default.warn(`Couldn't extract XMP data from file`);
169
+ logger_1.default.warn(err);
170
+ }
172
171
  }
173
172
  if (((_a = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _a === void 0 ? void 0 : _a.Caption) && typeof fullMetadata.iptc.Caption === 'string') {
174
173
  metadata.description = (_b = fullMetadata.iptc) === null || _b === void 0 ? void 0 : _b.Caption;
@@ -203,62 +202,19 @@ class FilesService extends items_1.ItemsService {
203
202
  */
204
203
  async importOne(importURL, body) {
205
204
  var _a, _b, _c;
206
- const axios = (await import('axios')).default;
207
205
  const fileCreatePermissions = (_b = (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.permissions) === null || _b === void 0 ? void 0 : _b.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
208
206
  if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
209
207
  throw new exceptions_1.ForbiddenException();
210
208
  }
211
- let resolvedUrl;
212
- try {
213
- resolvedUrl = new url_1.URL(importURL);
214
- }
215
- catch (err) {
216
- logger_1.default.warn(err, `Requested URL ${importURL} isn't a valid URL`);
217
- throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
218
- service: 'external-file',
219
- });
220
- }
221
- let ip = resolvedUrl.hostname;
222
- if (net_1.default.isIP(ip) === 0) {
223
- try {
224
- ip = (await lookupDNS(ip)).address;
225
- }
226
- catch (err) {
227
- logger_1.default.warn(err, `Couldn't lookup the DNS for url ${importURL}`);
228
- throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
229
- service: 'external-file',
230
- });
231
- }
232
- }
233
- if (env_1.default.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
234
- const networkInterfaces = os_1.default.networkInterfaces();
235
- for (const networkInfo of Object.values(networkInterfaces)) {
236
- if (!networkInfo)
237
- continue;
238
- for (const info of networkInfo) {
239
- if (info.address === ip) {
240
- logger_1.default.warn(`Requested URL ${importURL} resolves to localhost.`);
241
- throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
242
- service: 'external-file',
243
- });
244
- }
245
- }
246
- }
247
- }
248
- if (env_1.default.IMPORT_IP_DENY_LIST.includes(ip)) {
249
- logger_1.default.warn(`Requested URL ${importURL} resolves to a denied IP address.`);
250
- throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
251
- service: 'external-file',
252
- });
253
- }
254
209
  let fileResponse;
255
210
  try {
211
+ const axios = await (0, index_1.getAxios)();
256
212
  fileResponse = await axios.get((0, encodeurl_1.default)(importURL), {
257
213
  responseType: 'stream',
258
214
  });
259
215
  }
260
216
  catch (err) {
261
- logger_1.default.warn(err, `Couldn't fetch file from url "${importURL}"`);
217
+ logger_1.default.warn(err, `Couldn't fetch file from URL "${importURL}"`);
262
218
  throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
263
219
  service: 'external-file',
264
220
  });
@@ -9,9 +9,7 @@ const utils_1 = require("@directus/shared/utils");
9
9
  const argon2_1 = __importDefault(require("argon2"));
10
10
  const graphql_1 = require("graphql");
11
11
  const graphql_compose_1 = require("graphql-compose");
12
- const process_error_1 = __importDefault(require("./utils/process-error"));
13
12
  const lodash_1 = require("lodash");
14
- const ms_1 = __importDefault(require("ms"));
15
13
  const cache_1 = require("../../cache");
16
14
  const constants_1 = require("../../constants");
17
15
  const database_1 = __importDefault(require("../../database"));
@@ -46,14 +44,16 @@ const tfa_1 = require("../tfa");
46
44
  const users_1 = require("../users");
47
45
  const utils_2 = require("../utils");
48
46
  const webhooks_1 = require("../webhooks");
47
+ const process_error_1 = __importDefault(require("./utils/process-error"));
49
48
  const date_1 = require("./types/date");
50
49
  const geojson_1 = require("./types/geojson");
51
50
  const string_or_float_1 = require("./types/string-or-float");
52
51
  const void_1 = require("./types/void");
53
- const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
54
- const hash_1 = require("./types/hash");
55
- const bigint_1 = require("./types/bigint");
56
52
  const constants_2 = require("@directus/shared/constants");
53
+ const get_milliseconds_1 = require("../../utils/get-milliseconds");
54
+ const bigint_1 = require("./types/bigint");
55
+ const hash_1 = require("./types/hash");
56
+ const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
57
57
  const validationRules = Array.from(graphql_1.specifiedRules);
58
58
  if (env_1.default.GRAPHQL_INTROSPECTION === false) {
59
59
  validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
@@ -842,6 +842,12 @@ class GraphQLService {
842
842
  limit: {
843
843
  type: graphql_1.GraphQLInt,
844
844
  },
845
+ offset: {
846
+ type: graphql_1.GraphQLInt,
847
+ },
848
+ page: {
849
+ type: graphql_1.GraphQLInt,
850
+ },
845
851
  search: {
846
852
  type: graphql_1.GraphQLString,
847
853
  },
@@ -1614,7 +1620,7 @@ class GraphQLService {
1614
1620
  accountability: this.accountability,
1615
1621
  schema: this.schema,
1616
1622
  });
1617
- return await service.serverInfo();
1623
+ return await service.health();
1618
1624
  },
1619
1625
  },
1620
1626
  });
@@ -1656,7 +1662,7 @@ class GraphQLService {
1656
1662
  res === null || res === void 0 ? void 0 : res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
1657
1663
  httpOnly: true,
1658
1664
  domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
1659
- maxAge: (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL),
1665
+ maxAge: (0, get_milliseconds_1.getMilliseconds)(env_1.default.REFRESH_TOKEN_TTL),
1660
1666
  secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
1661
1667
  sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
1662
1668
  });
@@ -1695,7 +1701,7 @@ class GraphQLService {
1695
1701
  res === null || res === void 0 ? void 0 : res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
1696
1702
  httpOnly: true,
1697
1703
  domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
1698
- maxAge: (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL),
1704
+ maxAge: (0, get_milliseconds_1.getMilliseconds)(env_1.default.REFRESH_TOKEN_TTL),
1699
1705
  secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
1700
1706
  sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
1701
1707
  });
@@ -4,23 +4,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const logger_1 = __importDefault(require("../../../logger"));
7
+ const exceptions_1 = require("@directus/shared/exceptions");
7
8
  const processError = (accountability, error) => {
8
9
  logger_1.default.error(error);
9
- if ((accountability === null || accountability === void 0 ? void 0 : accountability.admin) === true) {
10
+ const { originalError } = error;
11
+ if (originalError instanceof exceptions_1.BaseException) {
10
12
  return {
11
- ...error,
13
+ message: originalError.message,
12
14
  extensions: {
13
- code: 'INTERNAL_SERVER_ERROR',
15
+ code: originalError.code,
16
+ ...originalError.extensions,
14
17
  },
15
18
  };
16
19
  }
17
20
  else {
18
- return {
19
- message: 'An unexpected error occurred.',
20
- extensions: {
21
- code: 'INTERNAL_SERVER_ERROR',
22
- },
23
- };
21
+ if ((accountability === null || accountability === void 0 ? void 0 : accountability.admin) === true) {
22
+ return {
23
+ ...error,
24
+ extensions: {
25
+ code: 'INTERNAL_SERVER_ERROR',
26
+ },
27
+ };
28
+ }
29
+ else {
30
+ return {
31
+ message: 'An unexpected error occurred.',
32
+ extensions: {
33
+ code: 'INTERNAL_SERVER_ERROR',
34
+ },
35
+ };
36
+ }
24
37
  }
25
38
  };
26
39
  exports.default = processError;
@@ -3,6 +3,7 @@ import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
3
3
  import { Knex } from 'knex';
4
4
  import { AbstractServiceOptions, File } from '../types';
5
5
  import type { Readable } from 'node:stream';
6
+ type ExportFormat = 'csv' | 'json' | 'xml' | 'yaml';
6
7
  export declare class ImportService {
7
8
  knex: Knex;
8
9
  accountability: Accountability | null;
@@ -22,14 +23,15 @@ export declare class ExportService {
22
23
  * until all the data is retrieved. Uploads the result as a new file using the regular
23
24
  * FilesService upload method.
24
25
  */
25
- exportToFile(collection: string, query: Partial<Query>, format: 'xml' | 'csv' | 'json', options?: {
26
+ exportToFile(collection: string, query: Partial<Query>, format: ExportFormat, options?: {
26
27
  file?: Partial<File>;
27
28
  }): Promise<void>;
28
29
  /**
29
30
  * Transform a given input object / array to the given type
30
31
  */
31
- transform(input: Record<string, any>[], format: 'xml' | 'csv' | 'json', options?: {
32
+ transform(input: Record<string, any>[], format: ExportFormat, options?: {
32
33
  includeHeader?: boolean;
33
34
  includeFooter?: boolean;
34
35
  }): string;
35
36
  }
37
+ export {};
@@ -9,6 +9,7 @@ const async_1 = require("async");
9
9
  const csv_parser_1 = __importDefault(require("csv-parser"));
10
10
  const destroy_1 = __importDefault(require("destroy"));
11
11
  const fs_extra_1 = require("fs-extra");
12
+ const js_yaml_1 = require("js-yaml");
12
13
  const js2xmlparser_1 = require("js2xmlparser");
13
14
  const json2csv_1 = require("json2csv");
14
15
  const lodash_1 = require("lodash");
@@ -23,6 +24,7 @@ const get_date_formatted_1 = require("../utils/get-date-formatted");
23
24
  const files_1 = require("./files");
24
25
  const items_1 = require("./items");
25
26
  const notifications_1 = require("./notifications");
27
+ const emitter_1 = __importDefault(require("../emitter"));
26
28
  class ImportService {
27
29
  constructor(options) {
28
30
  this.knex = options.knex || (0, database_1.default)();
@@ -50,6 +52,7 @@ class ImportService {
50
52
  }
51
53
  importJSON(collection, stream) {
52
54
  const extractJSON = StreamArray_1.default.withParser();
55
+ const nestedActionEvents = [];
53
56
  return this.knex.transaction((trx) => {
54
57
  const service = new items_1.ItemsService(collection, {
55
58
  knex: trx,
@@ -57,7 +60,7 @@ class ImportService {
57
60
  accountability: this.accountability,
58
61
  });
59
62
  const saveQueue = (0, async_1.queue)(async (value) => {
60
- return await service.upsertOne(value);
63
+ return await service.upsertOne(value, { bypassEmitAction: (params) => nestedActionEvents.push(params) });
61
64
  });
62
65
  return new Promise((resolve, reject) => {
63
66
  stream.pipe(extractJSON);
@@ -74,6 +77,9 @@ class ImportService {
74
77
  });
75
78
  extractJSON.on('end', () => {
76
79
  saveQueue.drain(() => {
80
+ for (const nestedActionEvent of nestedActionEvents) {
81
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
82
+ }
77
83
  return resolve();
78
84
  });
79
85
  });
@@ -81,6 +87,7 @@ class ImportService {
81
87
  });
82
88
  }
83
89
  importCSV(collection, stream) {
90
+ const nestedActionEvents = [];
84
91
  return this.knex.transaction((trx) => {
85
92
  const service = new items_1.ItemsService(collection, {
86
93
  knex: trx,
@@ -88,7 +95,7 @@ class ImportService {
88
95
  accountability: this.accountability,
89
96
  });
90
97
  const saveQueue = (0, async_1.queue)(async (value) => {
91
- return await service.upsertOne(value);
98
+ return await service.upsertOne(value, { bypassEmitAction: (action) => nestedActionEvents.push(action) });
92
99
  });
93
100
  return new Promise((resolve, reject) => {
94
101
  stream
@@ -122,6 +129,9 @@ class ImportService {
122
129
  })
123
130
  .on('end', () => {
124
131
  saveQueue.drain(() => {
132
+ for (const nestedActionEvent of nestedActionEvents) {
133
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
134
+ }
125
135
  return resolve();
126
136
  });
127
137
  });
@@ -148,9 +158,10 @@ class ExportService {
148
158
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
149
159
  try {
150
160
  const mimeTypes = {
151
- xml: 'text/xml',
152
161
  csv: 'text/csv',
153
162
  json: 'application/json',
163
+ xml: 'text/xml',
164
+ yaml: 'text/yaml',
154
165
  };
155
166
  const database = (0, database_1.default)();
156
167
  const { path, cleanup } = await (0, tmp_promise_1.file)();
@@ -277,6 +288,9 @@ class ExportService {
277
288
  }
278
289
  return string;
279
290
  }
291
+ if (format === 'yaml') {
292
+ return (0, js_yaml_1.dump)(input);
293
+ }
280
294
  throw new exceptions_1.ServiceUnavailableException(`Illegal export type used: "${format}"`, { service: 'export' });
281
295
  }
282
296
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -22,6 +22,7 @@ export * from './presets';
22
22
  export * from './relations';
23
23
  export * from './revisions';
24
24
  export * from './roles';
25
+ export * from './schema';
25
26
  export * from './server';
26
27
  export * from './settings';
27
28
  export * from './specifications';
@@ -39,6 +39,7 @@ __exportStar(require("./presets"), exports);
39
39
  __exportStar(require("./relations"), exports);
40
40
  __exportStar(require("./revisions"), exports);
41
41
  __exportStar(require("./roles"), exports);
42
+ __exportStar(require("./schema"), exports);
42
43
  __exportStar(require("./server"), exports);
43
44
  __exportStar(require("./settings"), exports);
44
45
  __exportStar(require("./specifications"), exports);
@@ -86,6 +86,9 @@ class ItemsService {
86
86
  const payloadWithPresets = this.accountability
87
87
  ? await authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
88
88
  : payloadAfterHooks;
89
+ if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
90
+ throw opts.preMutationException;
91
+ }
89
92
  const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
90
93
  const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
91
94
  const payloadWithoutAliases = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, ...aliases));
@@ -180,14 +183,19 @@ class ItemsService {
180
183
  accountability: this.accountability,
181
184
  },
182
185
  };
183
- if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
184
- emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
186
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
187
+ opts.bypassEmitAction(actionEvent);
185
188
  }
186
189
  else {
187
- opts.bypassEmitAction(actionEvent);
190
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
188
191
  }
189
192
  for (const nestedActionEvent of nestedActionEvents) {
190
- emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
193
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
194
+ opts.bypassEmitAction(nestedActionEvent);
195
+ }
196
+ else {
197
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
198
+ }
191
199
  }
192
200
  }
193
201
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
@@ -211,7 +219,7 @@ class ItemsService {
211
219
  const primaryKey = await service.createOne(payload, {
212
220
  ...(opts || {}),
213
221
  autoPurgeCache: false,
214
- bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
222
+ bypassEmitAction: (params) => nestedActionEvents.push(params),
215
223
  });
216
224
  primaryKeys.push(primaryKey);
217
225
  }
@@ -219,11 +227,11 @@ class ItemsService {
219
227
  });
220
228
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
221
229
  for (const nestedActionEvent of nestedActionEvents) {
222
- if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
223
- emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
230
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
231
+ opts.bypassEmitAction(nestedActionEvent);
224
232
  }
225
233
  else {
226
- opts.bypassEmitAction(nestedActionEvent);
234
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
227
235
  }
228
236
  }
229
237
  }
@@ -412,6 +420,9 @@ class ItemsService {
412
420
  const payloadWithPresets = this.accountability
413
421
  ? await authorizationService.validatePayload('update', this.collection, payloadAfterHooks)
414
422
  : payloadAfterHooks;
423
+ if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
424
+ throw opts.preMutationException;
425
+ }
415
426
  await this.knex.transaction(async (trx) => {
416
427
  const payloadService = new payload_1.PayloadService(this.collection, {
417
428
  accountability: this.accountability,
@@ -508,14 +519,19 @@ class ItemsService {
508
519
  accountability: this.accountability,
509
520
  },
510
521
  };
511
- if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
512
- emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
522
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
523
+ opts.bypassEmitAction(actionEvent);
513
524
  }
514
525
  else {
515
- opts.bypassEmitAction(actionEvent);
526
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
516
527
  }
517
528
  for (const nestedActionEvent of nestedActionEvents) {
518
- emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
529
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
530
+ opts.bypassEmitAction(nestedActionEvent);
531
+ }
532
+ else {
533
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
534
+ }
519
535
  }
520
536
  }
521
537
  return keys;
@@ -596,6 +612,9 @@ class ItemsService {
596
612
  });
597
613
  await authorizationService.checkAccess('delete', this.collection, keys);
598
614
  }
615
+ if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
616
+ throw opts.preMutationException;
617
+ }
599
618
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
600
619
  await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, keys, {
601
620
  collection: this.collection,
@@ -642,11 +661,11 @@ class ItemsService {
642
661
  accountability: this.accountability,
643
662
  },
644
663
  };
645
- if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
646
- emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
664
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
665
+ opts.bypassEmitAction(actionEvent);
647
666
  }
648
667
  else {
649
- opts.bypassEmitAction(actionEvent);
668
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
650
669
  }
651
670
  }
652
671
  return keys;
@@ -155,6 +155,7 @@ class RelationsService {
155
155
  throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
156
156
  }
157
157
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
158
+ this.helpers.schema.preRelationChange(relation);
158
159
  const nestedActionEvents = [];
159
160
  try {
160
161
  const metaRow = {
@@ -225,6 +226,7 @@ class RelationsService {
225
226
  throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
226
227
  }
227
228
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
229
+ this.helpers.schema.preRelationChange(relation);
228
230
  const nestedActionEvents = [];
229
231
  try {
230
232
  await this.knex.transaction(async (trx) => {