@strapi/data-transfer 4.6.0-beta.2 → 4.6.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 (43) hide show
  1. package/lib/engine/diagnostic.d.ts +40 -0
  2. package/lib/engine/diagnostic.js +50 -0
  3. package/lib/engine/errors.d.ts +28 -0
  4. package/lib/engine/errors.js +29 -0
  5. package/lib/engine/index.d.ts +11 -3
  6. package/lib/engine/index.js +143 -30
  7. package/lib/engine/validation/index.d.ts +2 -1
  8. package/lib/engine/validation/index.js +4 -13
  9. package/lib/engine/validation/provider.d.ts +3 -0
  10. package/lib/engine/validation/provider.js +18 -0
  11. package/lib/errors/base.d.ts +8 -0
  12. package/lib/errors/base.js +13 -0
  13. package/lib/errors/constants.d.ts +3 -0
  14. package/lib/errors/constants.js +9 -0
  15. package/lib/errors/index.d.ts +2 -0
  16. package/lib/errors/index.js +19 -0
  17. package/lib/errors/providers.d.ts +21 -0
  18. package/lib/errors/providers.js +32 -0
  19. package/lib/file/providers/source/index.js +29 -14
  20. package/lib/strapi/providers/local-destination/index.d.ts +3 -1
  21. package/lib/strapi/providers/local-destination/index.js +53 -33
  22. package/lib/strapi/providers/local-destination/strategies/restore/configuration.d.ts +2 -2
  23. package/lib/strapi/providers/local-destination/strategies/restore/configuration.js +17 -10
  24. package/lib/strapi/providers/local-destination/strategies/restore/entities.d.ts +2 -0
  25. package/lib/strapi/providers/local-destination/strategies/restore/entities.js +57 -54
  26. package/lib/strapi/providers/local-destination/strategies/restore/index.js +2 -1
  27. package/lib/strapi/providers/local-destination/strategies/restore/links.d.ts +2 -1
  28. package/lib/strapi/providers/local-destination/strategies/restore/links.js +18 -15
  29. package/lib/strapi/providers/local-source/index.js +6 -21
  30. package/lib/strapi/providers/remote-destination/index.js +21 -6
  31. package/lib/strapi/queries/link.d.ts +1 -1
  32. package/lib/strapi/queries/link.js +17 -3
  33. package/lib/strapi/remote/constants.d.ts +1 -0
  34. package/lib/strapi/remote/constants.js +2 -1
  35. package/lib/strapi/remote/handlers.js +28 -12
  36. package/lib/utils/index.d.ts +1 -0
  37. package/lib/utils/index.js +2 -1
  38. package/lib/utils/json.js +6 -6
  39. package/lib/utils/providers.d.ts +2 -0
  40. package/lib/utils/providers.js +11 -0
  41. package/lib/utils/transaction.d.ts +3 -0
  42. package/lib/utils/transaction.js +70 -0
  43. package/package.json +7 -6
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProviderTransferError = exports.ProviderValidationError = exports.ProviderInitializationError = exports.ProviderError = void 0;
4
+ const base_1 = require("./base");
5
+ const constants_1 = require("./constants");
6
+ class ProviderError extends base_1.DataTransferError {
7
+ constructor(severity, message, details) {
8
+ super('provider', severity, message, details);
9
+ }
10
+ }
11
+ exports.ProviderError = ProviderError;
12
+ class ProviderInitializationError extends ProviderError {
13
+ constructor(message) {
14
+ super(constants_1.SeverityKind.FATAL, message, { step: 'initialization' });
15
+ }
16
+ }
17
+ exports.ProviderInitializationError = ProviderInitializationError;
18
+ // TODO: these types are not working correctly, ProviderTransferError() is accepting any details object rather than requiring T
19
+ class ProviderValidationError extends ProviderError {
20
+ constructor(message, details) {
21
+ super(constants_1.SeverityKind.SILLY, message, { step: 'validation', details });
22
+ }
23
+ }
24
+ exports.ProviderValidationError = ProviderValidationError;
25
+ // TODO: these types are not working correctly, ProviderTransferError() is accepting any details object rather than requiring T
26
+ class ProviderTransferError extends ProviderError {
27
+ constructor(message, details) {
28
+ super(constants_1.SeverityKind.FATAL, message, { step: 'transfer', details });
29
+ }
30
+ }
31
+ exports.ProviderTransferError = ProviderTransferError;
32
+ //# sourceMappingURL=providers.js.map
@@ -26,6 +26,7 @@ const stream_1 = require("stream");
26
26
  const Parser_1 = require("stream-json/jsonl/Parser");
27
27
  const encryption_1 = require("../../../utils/encryption");
28
28
  const stream_2 = require("../../../utils/stream");
29
+ const providers_1 = require("../../../errors/providers");
29
30
  /**
30
31
  * Constant for the metadata file path
31
32
  */
@@ -56,7 +57,10 @@ class LocalFileSourceProvider {
56
57
  __classPrivateFieldSet(this, _LocalFileSourceProvider_metadata, await this.getMetadata(), "f");
57
58
  }
58
59
  catch (e) {
59
- throw new Error(`Can't read file "${filePath}".`);
60
+ if (this.options?.encryption?.enabled) {
61
+ throw new providers_1.ProviderInitializationError(`Key is incorrect or the file '${filePath}' is not a valid Strapi data file.`);
62
+ }
63
+ throw new providers_1.ProviderInitializationError(`File '${filePath}' is not a valid Strapi data file.`);
60
64
  }
61
65
  }
62
66
  getMetadata() {
@@ -137,35 +141,46 @@ _LocalFileSourceProvider_metadata = new WeakMap(), _LocalFileSourceProvider_inst
137
141
  if (entry.type !== 'File') {
138
142
  return false;
139
143
  }
140
- const parts = filePath.split('/');
144
+ const parts = path_1.default.relative('.', filePath).split('/');
145
+ // TODO: this method is limiting us from having additional subdirectories and is requiring us to remove any "./" prefixes (the path.relative line above)
141
146
  if (parts.length !== 2) {
142
147
  return false;
143
148
  }
144
149
  return parts[0] === directory;
145
150
  },
146
- onentry(entry) {
151
+ async onentry(entry) {
147
152
  const transforms = [
148
153
  // JSONL parser to read the data chunks one by one (line by line)
149
- (0, Parser_1.parser)(),
154
+ (0, Parser_1.parser)({
155
+ checkErrors: true,
156
+ }),
150
157
  // The JSONL parser returns each line as key/value
151
158
  (line) => line.value,
152
159
  ];
153
- entry
154
- // Pipe transforms
155
- .pipe((0, stream_chain_1.chain)(transforms))
156
- // Pipe the out stream to the whole pipeline
157
- // DO NOT send the 'end' event when this entry has finished
158
- // emitting data, so that it doesn't close the out stream
159
- .pipe(outStream, { end: false });
160
+ const stream = entry.pipe((0, stream_chain_1.chain)(transforms));
161
+ try {
162
+ for await (const chunk of stream) {
163
+ outStream.write(chunk);
164
+ }
165
+ }
166
+ catch (e) {
167
+ outStream.destroy(new providers_1.ProviderTransferError(`Error parsing backup files from backup file ${entry.path}: ${e.message}`, {
168
+ details: {
169
+ error: e,
170
+ },
171
+ }));
172
+ }
160
173
  },
161
174
  }),
162
- ], () => {
175
+ ], async () => {
163
176
  // Manually send the 'end' event to the out stream
164
177
  // once every entry has finished streaming its content
165
178
  outStream.end();
166
179
  });
167
180
  return outStream;
168
- }, _LocalFileSourceProvider_parseJSONFile = async function _LocalFileSourceProvider_parseJSONFile(fileStream, filePath) {
181
+ }, _LocalFileSourceProvider_parseJSONFile =
182
+ // For collecting an entire JSON file then parsing it, not for streaming JSONL
183
+ async function _LocalFileSourceProvider_parseJSONFile(fileStream, filePath) {
169
184
  return new Promise((resolve, reject) => {
170
185
  (0, stream_1.pipeline)([
171
186
  fileStream,
@@ -175,7 +190,7 @@ _LocalFileSourceProvider_metadata = new WeakMap(), _LocalFileSourceProvider_inst
175
190
  * Filter the parsed entries to only keep the one that matches the given filepath
176
191
  */
177
192
  filter(entryPath, entry) {
178
- return entryPath === filePath && entry.type === 'File';
193
+ return !path_1.default.relative(filePath, entryPath).length && entry.type === 'File';
179
194
  },
180
195
  async onentry(entry) {
181
196
  // Collect all the content of the entry file
@@ -1,7 +1,7 @@
1
1
  /// <reference types="lodash" />
2
2
  /// <reference types="node" />
3
3
  import { Writable } from 'stream';
4
- import type { IDestinationProvider, IMetadata, ProviderType } from '../../../../types';
4
+ import type { IDestinationProvider, IMetadata, ProviderType, Transaction } from '../../../../types';
5
5
  import { restore } from './strategies';
6
6
  export declare const VALID_CONFLICT_STRATEGIES: string[];
7
7
  export declare const DEFAULT_CONFLICT_STRATEGY = "restore";
@@ -17,9 +17,11 @@ declare class LocalStrapiDestinationProvider implements IDestinationProvider {
17
17
  type: ProviderType;
18
18
  options: ILocalStrapiDestinationProviderOptions;
19
19
  strapi?: Strapi.Strapi;
20
+ transaction?: Transaction;
20
21
  constructor(options: ILocalStrapiDestinationProviderOptions);
21
22
  bootstrap(): Promise<void>;
22
23
  close(): Promise<void>;
24
+ rollback(): void;
23
25
  beforeTransfer(): Promise<void>;
24
26
  getMetadata(): IMetadata;
25
27
  getSchemas(): import("lodash").Dictionary<Partial<import("@strapi/strapi").Schema>>;
@@ -44,6 +44,8 @@ const path_1 = __importDefault(require("path"));
44
44
  const fse = __importStar(require("fs-extra"));
45
45
  const strategies_1 = require("./strategies");
46
46
  const utils = __importStar(require("../../../utils"));
47
+ const providers_1 = require("../../../errors/providers");
48
+ const providers_2 = require("../../../utils/providers");
47
49
  exports.VALID_CONFLICT_STRATEGIES = ['restore', 'merge'];
48
50
  exports.DEFAULT_CONFLICT_STRATEGY = 'restore';
49
51
  class LocalStrapiDestinationProvider {
@@ -61,39 +63,46 @@ class LocalStrapiDestinationProvider {
61
63
  async bootstrap() {
62
64
  __classPrivateFieldGet(this, _LocalStrapiDestinationProvider_instances, "m", _LocalStrapiDestinationProvider_validateOptions).call(this);
63
65
  this.strapi = await this.options.getStrapi();
66
+ this.transaction = utils.transaction.createTransaction(this.strapi);
64
67
  }
65
68
  async close() {
66
69
  const { autoDestroy } = this.options;
70
+ this.transaction?.end();
67
71
  // Basically `!== false` but more deterministic
68
72
  if (autoDestroy === undefined || autoDestroy === true) {
69
73
  await this.strapi?.destroy();
70
74
  }
71
75
  }
76
+ rollback() {
77
+ this.transaction?.rollback();
78
+ }
72
79
  async beforeTransfer() {
73
- if (this.options.strategy === 'restore') {
74
- await __classPrivateFieldGet(this, _LocalStrapiDestinationProvider_instances, "m", _LocalStrapiDestinationProvider_deleteAll).call(this);
80
+ if (!this.strapi) {
81
+ throw new Error('Strapi instance not found');
75
82
  }
83
+ await this.transaction?.attach(async () => {
84
+ try {
85
+ if (this.options.strategy === 'restore') {
86
+ await __classPrivateFieldGet(this, _LocalStrapiDestinationProvider_instances, "m", _LocalStrapiDestinationProvider_deleteAll).call(this);
87
+ }
88
+ }
89
+ catch (error) {
90
+ throw new Error(`restore failed ${error}`);
91
+ }
92
+ });
76
93
  }
77
94
  getMetadata() {
78
95
  const strapiVersion = strapi.config.get('info.strapi');
79
96
  const createdAt = new Date().toISOString();
80
- const plugins = Object.keys(strapi.plugins);
81
97
  return {
82
98
  createdAt,
83
99
  strapi: {
84
100
  version: strapiVersion,
85
- plugins: plugins.map((name) => ({
86
- name,
87
- // TODO: Get the plugin actual version when it'll be available
88
- version: strapiVersion,
89
- })),
90
101
  },
91
102
  };
92
103
  }
93
104
  getSchemas() {
94
- if (!this.strapi) {
95
- throw new Error('Not able to get Schemas. Strapi instance not found');
96
- }
105
+ (0, providers_2.assertValidStrapi)(this.strapi, 'Not able to get Schemas');
97
106
  const schemas = {
98
107
  ...this.strapi.contentTypes,
99
108
  ...this.strapi.components,
@@ -101,9 +110,7 @@ class LocalStrapiDestinationProvider {
101
110
  return utils.schema.mapSchemasValues(schemas);
102
111
  }
103
112
  createEntitiesWriteStream() {
104
- if (!this.strapi) {
105
- throw new Error('Not able to import entities. Strapi instance not found');
106
- }
113
+ (0, providers_2.assertValidStrapi)(this.strapi, 'Not able to import entities');
107
114
  const { strategy } = this.options;
108
115
  const updateMappingTable = (type, oldID, newID) => {
109
116
  if (!__classPrivateFieldGet(this, _LocalStrapiDestinationProvider_entitiesMapper, "f")[type]) {
@@ -115,19 +122,24 @@ class LocalStrapiDestinationProvider {
115
122
  return strategies_1.restore.createEntitiesWriteStream({
116
123
  strapi: this.strapi,
117
124
  updateMappingTable,
125
+ transaction: this.transaction,
118
126
  });
119
127
  }
120
- throw new Error(`Invalid strategy supplied: "${strategy}"`);
128
+ throw new providers_1.ProviderValidationError(`Invalid strategy ${this.options.strategy}`, {
129
+ check: 'strategy',
130
+ strategy: this.options.strategy,
131
+ validStrategies: exports.VALID_CONFLICT_STRATEGIES,
132
+ });
121
133
  }
122
134
  // TODO: Move this logic to the restore strategy
123
135
  async createAssetsWriteStream() {
124
- if (!this.strapi) {
125
- throw new Error('Not able to stream Assets. Strapi instance not found');
126
- }
136
+ (0, providers_2.assertValidStrapi)(this.strapi, 'Not able to stream Assets');
127
137
  const assetsDirectory = path_1.default.join(this.strapi.dirs.static.public, 'uploads');
128
138
  const backupDirectory = path_1.default.join(this.strapi.dirs.static.public, `uploads_backup_${Date.now()}`);
129
- await fse.rename(assetsDirectory, backupDirectory);
139
+ await fse.move(assetsDirectory, backupDirectory);
130
140
  await fse.mkdir(assetsDirectory);
141
+ // Create a .gitkeep file to ensure the directory is not empty
142
+ await fse.outputFile(path_1.default.join(assetsDirectory, '.gitkeep'), '');
131
143
  return new stream_1.Writable({
132
144
  objectMode: true,
133
145
  async final(next) {
@@ -143,11 +155,11 @@ class LocalStrapiDestinationProvider {
143
155
  .on('error', async (error) => {
144
156
  try {
145
157
  await fse.rm(assetsDirectory, { recursive: true, force: true });
146
- await fse.rename(backupDirectory, assetsDirectory);
147
- this.destroy(new Error(`There was an error during the transfer process. The original files have been restored to ${assetsDirectory}`));
158
+ await fse.move(backupDirectory, assetsDirectory);
159
+ this.destroy(new providers_1.ProviderTransferError(`There was an error during the transfer process. The original files have been restored to ${assetsDirectory}`));
148
160
  }
149
161
  catch (err) {
150
- throw new Error(`There was an error doing the rollback process. The original files are in ${backupDirectory}, but we failed to restore them to ${assetsDirectory}`);
162
+ throw new providers_1.ProviderTransferError(`There was an error doing the rollback process. The original files are in ${backupDirectory}, but we failed to restore them to ${assetsDirectory}`);
151
163
  }
152
164
  finally {
153
165
  callback(error);
@@ -157,14 +169,16 @@ class LocalStrapiDestinationProvider {
157
169
  });
158
170
  }
159
171
  async createConfigurationWriteStream() {
160
- if (!this.strapi) {
161
- throw new Error('Not able to stream Configurations. Strapi instance not found');
162
- }
172
+ (0, providers_2.assertValidStrapi)(this.strapi, 'Not able to stream Configurations');
163
173
  const { strategy } = this.options;
164
174
  if (strategy === 'restore') {
165
- return strategies_1.restore.createConfigurationWriteStream(this.strapi);
175
+ return strategies_1.restore.createConfigurationWriteStream(this.strapi, this.transaction);
166
176
  }
167
- throw new Error(`Invalid strategy supplied: "${strategy}"`);
177
+ throw new providers_1.ProviderValidationError(`Invalid strategy ${strategy}`, {
178
+ check: 'strategy',
179
+ strategy,
180
+ validStrategies: exports.VALID_CONFLICT_STRATEGIES,
181
+ });
168
182
  }
169
183
  async createLinksWriteStream() {
170
184
  if (!this.strapi) {
@@ -173,19 +187,25 @@ class LocalStrapiDestinationProvider {
173
187
  const { strategy } = this.options;
174
188
  const mapID = (uid, id) => __classPrivateFieldGet(this, _LocalStrapiDestinationProvider_entitiesMapper, "f")[uid]?.[id];
175
189
  if (strategy === 'restore') {
176
- return strategies_1.restore.createLinksWriteStream(mapID, this.strapi);
190
+ return strategies_1.restore.createLinksWriteStream(mapID, this.strapi, this.transaction);
177
191
  }
178
- throw new Error(`Invalid strategy supplied: "${strategy}"`);
192
+ throw new providers_1.ProviderValidationError(`Invalid strategy ${strategy}`, {
193
+ check: 'strategy',
194
+ strategy,
195
+ validStrategies: exports.VALID_CONFLICT_STRATEGIES,
196
+ });
179
197
  }
180
198
  }
181
199
  _LocalStrapiDestinationProvider_entitiesMapper = new WeakMap(), _LocalStrapiDestinationProvider_instances = new WeakSet(), _LocalStrapiDestinationProvider_validateOptions = function _LocalStrapiDestinationProvider_validateOptions() {
182
200
  if (!exports.VALID_CONFLICT_STRATEGIES.includes(this.options.strategy)) {
183
- throw new Error(`Invalid stategy ${this.options.strategy}`);
201
+ throw new providers_1.ProviderValidationError(`Invalid strategy ${this.options.strategy}`, {
202
+ check: 'strategy',
203
+ strategy: this.options.strategy,
204
+ validStrategies: exports.VALID_CONFLICT_STRATEGIES,
205
+ });
184
206
  }
185
207
  }, _LocalStrapiDestinationProvider_deleteAll = async function _LocalStrapiDestinationProvider_deleteAll() {
186
- if (!this.strapi) {
187
- throw new Error('Strapi instance not found');
188
- }
208
+ (0, providers_2.assertValidStrapi)(this.strapi);
189
209
  return strategies_1.restore.deleteRecords(this.strapi, this.options.restore);
190
210
  };
191
211
  const createLocalStrapiDestinationProvider = (options) => {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
2
  import { Writable } from 'stream';
3
- import { IConfiguration } from '../../../../../../types';
3
+ import { IConfiguration, Transaction } from '../../../../../../types';
4
4
  export declare const restoreConfigs: (strapi: Strapi.Strapi, config: IConfiguration) => Promise<any>;
5
- export declare const createConfigurationWriteStream: (strapi: Strapi.Strapi) => Promise<Writable>;
5
+ export declare const createConfigurationWriteStream: (strapi: Strapi.Strapi, transaction?: Transaction | undefined) => Promise<Writable>;
@@ -4,9 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createConfigurationWriteStream = exports.restoreConfigs = void 0;
7
+ const fp_1 = require("lodash/fp");
7
8
  const stream_1 = require("stream");
8
9
  const chalk_1 = __importDefault(require("chalk"));
9
- const restoreCoreStore = async (strapi, data) => {
10
+ const providers_1 = require("../../../../../errors/providers");
11
+ const omitInvalidCreationAttributes = (0, fp_1.omit)(['id']);
12
+ const restoreCoreStore = async (strapi, values) => {
13
+ const data = omitInvalidCreationAttributes(values);
10
14
  return strapi.db.query('strapi::core-store').create({
11
15
  data: {
12
16
  ...data,
@@ -14,7 +18,8 @@ const restoreCoreStore = async (strapi, data) => {
14
18
  },
15
19
  });
16
20
  };
17
- const restoreWebhooks = async (strapi, data) => {
21
+ const restoreWebhooks = async (strapi, values) => {
22
+ const data = omitInvalidCreationAttributes(values);
18
23
  return strapi.db.query('webhook').create({ data });
19
24
  };
20
25
  const restoreConfigs = async (strapi, config) => {
@@ -26,17 +31,19 @@ const restoreConfigs = async (strapi, config) => {
26
31
  }
27
32
  };
28
33
  exports.restoreConfigs = restoreConfigs;
29
- const createConfigurationWriteStream = async (strapi) => {
34
+ const createConfigurationWriteStream = async (strapi, transaction) => {
30
35
  return new stream_1.Writable({
31
36
  objectMode: true,
32
37
  async write(config, _encoding, callback) {
33
- try {
34
- await (0, exports.restoreConfigs)(strapi, config);
35
- }
36
- catch (error) {
37
- return callback(new Error(`Failed to import ${chalk_1.default.yellowBright(config.type)} (${chalk_1.default.greenBright(config.value.id)}`));
38
- }
39
- callback();
38
+ await transaction?.attach(async () => {
39
+ try {
40
+ await (0, exports.restoreConfigs)(strapi, config);
41
+ }
42
+ catch (error) {
43
+ return callback(new providers_1.ProviderTransferError(`Failed to import ${chalk_1.default.yellowBright(config.type)} (${chalk_1.default.greenBright(config.value.id)}`));
44
+ }
45
+ callback();
46
+ });
40
47
  },
41
48
  });
42
49
  };
@@ -1,9 +1,11 @@
1
1
  /// <reference types="node" />
2
2
  import type { SchemaUID } from '@strapi/strapi/lib/types/utils';
3
3
  import { Writable } from 'stream';
4
+ import type { Transaction } from '../../../../../../types';
4
5
  interface IEntitiesRestoreStreamOptions {
5
6
  strapi: Strapi.Strapi;
6
7
  updateMappingTable<T extends SchemaUID | string>(type: T, oldID: number, newID: number): void;
8
+ transaction?: Transaction;
7
9
  }
8
10
  declare const createEntitiesWriteStream: (options: IEntitiesRestoreStreamOptions) => Writable;
9
11
  export { createEntitiesWriteStream };
@@ -26,73 +26,76 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.createEntitiesWriteStream = void 0;
27
27
  const fp_1 = require("lodash/fp");
28
28
  const stream_1 = require("stream");
29
+ const providers_1 = require("../../../../../errors/providers");
29
30
  const utils_1 = require("../../../../../utils");
30
31
  const queries = __importStar(require("../../../../queries"));
31
32
  const createEntitiesWriteStream = (options) => {
32
- const { strapi, updateMappingTable } = options;
33
+ const { strapi, updateMappingTable, transaction } = options;
33
34
  const query = queries.entity.createEntityQuery(strapi);
34
35
  return new stream_1.Writable({
35
36
  objectMode: true,
36
37
  async write(entity, _encoding, callback) {
37
- const { type, id, data } = entity;
38
- const { create, getDeepPopulateComponentLikeQuery } = query(type);
39
- const contentType = strapi.getModel(type);
40
- /**
41
- * Resolve the component UID of an entity's attribute based
42
- * on a given path (components & dynamic zones only)
43
- */
44
- const resolveType = (paths) => {
45
- let cType = contentType;
46
- let value = data;
47
- for (const path of paths) {
48
- value = (0, fp_1.get)(path, value);
49
- // Needed when the value of cType should be computed
50
- // based on the next value (eg: dynamic zones)
51
- if (typeof cType === 'function') {
52
- cType = cType(value);
53
- }
54
- if (path in cType.attributes) {
55
- const attribute = cType.attributes[path];
56
- if (attribute.type === 'component') {
57
- cType = strapi.getModel(attribute.component);
38
+ await transaction?.attach(async () => {
39
+ const { type, id, data } = entity;
40
+ const { create, getDeepPopulateComponentLikeQuery } = query(type);
41
+ const contentType = strapi.getModel(type);
42
+ /**
43
+ * Resolve the component UID of an entity's attribute based
44
+ * on a given path (components & dynamic zones only)
45
+ */
46
+ const resolveType = (paths) => {
47
+ let cType = contentType;
48
+ let value = data;
49
+ for (const path of paths) {
50
+ value = (0, fp_1.get)(path, value);
51
+ // Needed when the value of cType should be computed
52
+ // based on the next value (eg: dynamic zones)
53
+ if (typeof cType === 'function') {
54
+ cType = cType(value);
58
55
  }
59
- if (attribute.type === 'dynamiczone') {
60
- cType = ({ __component }) => strapi.getModel(__component);
56
+ if (path in cType.attributes) {
57
+ const attribute = cType.attributes[path];
58
+ if (attribute.type === 'component') {
59
+ cType = strapi.getModel(attribute.component);
60
+ }
61
+ if (attribute.type === 'dynamiczone') {
62
+ cType = ({ __component }) => strapi.getModel(__component);
63
+ }
61
64
  }
62
65
  }
63
- }
64
- return cType?.uid;
65
- };
66
- try {
67
- const created = await create({
68
- data,
69
- populate: getDeepPopulateComponentLikeQuery(contentType, { select: 'id' }),
70
- select: 'id',
71
- });
72
- // Compute differences between original & new entities
73
- const diffs = utils_1.json.diff(data, created);
74
- updateMappingTable(type, id, created.id);
75
- // For each difference found on an ID attribute,
76
- // update the mapping the table accordingly
77
- diffs.forEach((diff) => {
78
- if (diff.kind === 'modified' && (0, fp_1.last)(diff.path) === 'id') {
79
- const target = resolveType(diff.path);
80
- // If no type is found for the given path, then ignore the diff
81
- if (!target) {
82
- return;
66
+ return cType?.uid;
67
+ };
68
+ try {
69
+ const created = await create({
70
+ data,
71
+ populate: getDeepPopulateComponentLikeQuery(contentType, { select: 'id' }),
72
+ select: 'id',
73
+ });
74
+ // Compute differences between original & new entities
75
+ const diffs = utils_1.json.diff(data, created);
76
+ updateMappingTable(type, id, created.id);
77
+ // For each difference found on an ID attribute,
78
+ // update the mapping the table accordingly
79
+ diffs.forEach((diff) => {
80
+ if (diff.kind === 'modified' && (0, fp_1.last)(diff.path) === 'id') {
81
+ const target = resolveType(diff.path);
82
+ // If no type is found for the given path, then ignore the diff
83
+ if (!target) {
84
+ return;
85
+ }
86
+ const [oldID, newID] = diff.values;
87
+ updateMappingTable(target, oldID, newID);
83
88
  }
84
- const [oldID, newID] = diff.values;
85
- updateMappingTable(target, oldID, newID);
89
+ });
90
+ }
91
+ catch (e) {
92
+ if (e instanceof Error) {
93
+ return callback(e);
86
94
  }
87
- });
88
- }
89
- catch (e) {
90
- if (e instanceof Error) {
91
- return callback(e);
95
+ return callback(new providers_1.ProviderTransferError(`Failed to create "${type}" (${id})`));
92
96
  }
93
- return callback(new Error(`Failed to create "${type}" (${id})`));
94
- }
95
- return callback(null);
97
+ return callback(null);
98
+ });
96
99
  },
97
100
  });
98
101
  };
@@ -27,6 +27,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.deleteRecords = void 0;
30
+ const providers_1 = require("../../../../../errors/providers");
30
31
  const queries = __importStar(require("../../../../queries"));
31
32
  const deleteRecords = async (strapi, options) => {
32
33
  const entities = await deleteEntitiesRecord(strapi, options);
@@ -92,7 +93,7 @@ const useResults = (keys) => {
92
93
  const update = (count, key) => {
93
94
  if (key) {
94
95
  if (!(key in results.aggregate)) {
95
- throw new Error(`Unknown key "${key}" provided in results update`);
96
+ throw new providers_1.ProviderTransferError(`Unknown key "${key}" provided in results update`);
96
97
  }
97
98
  results.aggregate[key].count += count;
98
99
  }
@@ -1,3 +1,4 @@
1
1
  /// <reference types="node" />
2
2
  import { Writable } from 'stream';
3
- export declare const createLinksWriteStream: (mapID: (uid: string, id: number) => number | undefined, strapi: Strapi.Strapi) => Writable;
3
+ import { Transaction } from '../../../../../../types';
4
+ export declare const createLinksWriteStream: (mapID: (uid: string, id: number) => number | undefined, strapi: Strapi.Strapi, transaction?: Transaction | undefined) => Writable;
@@ -2,26 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createLinksWriteStream = void 0;
4
4
  const stream_1 = require("stream");
5
+ const providers_1 = require("../../../../../errors/providers");
5
6
  const link_1 = require("../../../../queries/link");
6
- const createLinksWriteStream = (mapID, strapi) => {
7
+ const createLinksWriteStream = (mapID, strapi, transaction) => {
7
8
  return new stream_1.Writable({
8
9
  objectMode: true,
9
10
  async write(link, _encoding, callback) {
10
- const { left, right } = link;
11
- const query = (0, link_1.createLinkQuery)(strapi);
12
- // Map IDs if needed
13
- left.ref = mapID(left.type, left.ref) ?? left.ref;
14
- right.ref = mapID(right.type, right.ref) ?? right.ref;
15
- try {
16
- await query().insert(link);
17
- }
18
- catch (e) {
19
- if (e instanceof Error) {
20
- return callback(e);
11
+ await transaction?.attach(async (trx) => {
12
+ const { left, right } = link;
13
+ const query = (0, link_1.createLinkQuery)(strapi, trx);
14
+ // Map IDs if needed
15
+ left.ref = mapID(left.type, left.ref) ?? left.ref;
16
+ right.ref = mapID(right.type, right.ref) ?? right.ref;
17
+ try {
18
+ await query().insert(link);
21
19
  }
22
- return callback(new Error(`An error happened while trying to import a ${left.type} link. ${e}`));
23
- }
24
- callback(null);
20
+ catch (e) {
21
+ if (e instanceof Error) {
22
+ return callback(e);
23
+ }
24
+ return callback(new providers_1.ProviderTransferError(`An error happened while trying to import a ${left.type} link.`));
25
+ }
26
+ callback(null);
27
+ });
25
28
  },
26
29
  });
27
30
  };