@strapi/data-transfer 4.6.0-beta.2 → 4.6.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.
- package/lib/engine/diagnostic.d.ts +40 -0
- package/lib/engine/diagnostic.js +50 -0
- package/lib/engine/errors.d.ts +28 -0
- package/lib/engine/errors.js +29 -0
- package/lib/engine/index.d.ts +11 -3
- package/lib/engine/index.js +140 -30
- package/lib/engine/validation/index.d.ts +2 -1
- package/lib/engine/validation/index.js +4 -13
- package/lib/engine/validation/provider.d.ts +3 -0
- package/lib/engine/validation/provider.js +18 -0
- package/lib/errors/base.d.ts +8 -0
- package/lib/errors/base.js +13 -0
- package/lib/errors/constants.d.ts +3 -0
- package/lib/errors/constants.js +9 -0
- package/lib/errors/index.d.ts +2 -0
- package/lib/errors/index.js +19 -0
- package/lib/errors/providers.d.ts +21 -0
- package/lib/errors/providers.js +32 -0
- package/lib/file/providers/source/index.js +9 -11
- package/lib/strapi/providers/local-destination/index.d.ts +3 -1
- package/lib/strapi/providers/local-destination/index.js +51 -31
- package/lib/strapi/providers/local-destination/strategies/restore/configuration.d.ts +2 -2
- package/lib/strapi/providers/local-destination/strategies/restore/configuration.js +17 -10
- package/lib/strapi/providers/local-destination/strategies/restore/entities.d.ts +2 -0
- package/lib/strapi/providers/local-destination/strategies/restore/entities.js +57 -54
- package/lib/strapi/providers/local-destination/strategies/restore/index.js +2 -1
- package/lib/strapi/providers/local-destination/strategies/restore/links.d.ts +2 -1
- package/lib/strapi/providers/local-destination/strategies/restore/links.js +18 -15
- package/lib/strapi/providers/local-source/index.js +6 -21
- package/lib/strapi/providers/remote-destination/index.js +21 -6
- package/lib/strapi/queries/link.d.ts +1 -1
- package/lib/strapi/queries/link.js +17 -3
- package/lib/strapi/remote/constants.d.ts +1 -0
- package/lib/strapi/remote/constants.js +2 -1
- package/lib/strapi/remote/handlers.js +28 -12
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +2 -1
- package/lib/utils/providers.d.ts +2 -0
- package/lib/utils/providers.js +11 -0
- package/lib/utils/transaction.d.ts +3 -0
- package/lib/utils/transaction.js +70 -0
- package/package.json +6 -5
|
@@ -12,6 +12,7 @@ const uuid_1 = require("uuid");
|
|
|
12
12
|
const stream_1 = require("stream");
|
|
13
13
|
const utils_1 = require("./utils");
|
|
14
14
|
const constants_1 = require("../../remote/constants");
|
|
15
|
+
const providers_1 = require("../../../errors/providers");
|
|
15
16
|
class RemoteStrapiDestinationProvider {
|
|
16
17
|
constructor(options) {
|
|
17
18
|
_RemoteStrapiDestinationProvider_instances.add(this);
|
|
@@ -33,7 +34,7 @@ class RemoteStrapiDestinationProvider {
|
|
|
33
34
|
});
|
|
34
35
|
const res = (await query);
|
|
35
36
|
if (!res?.transferID) {
|
|
36
|
-
return reject(new
|
|
37
|
+
return reject(new providers_1.ProviderTransferError('Init failed, invalid response from the server'));
|
|
37
38
|
}
|
|
38
39
|
resolve(res.transferID);
|
|
39
40
|
})
|
|
@@ -42,12 +43,20 @@ class RemoteStrapiDestinationProvider {
|
|
|
42
43
|
}
|
|
43
44
|
async bootstrap() {
|
|
44
45
|
const { url, auth } = this.options;
|
|
46
|
+
const validProtocols = ['https:', 'http:'];
|
|
45
47
|
let ws;
|
|
46
|
-
if (!
|
|
47
|
-
throw new
|
|
48
|
+
if (!validProtocols.includes(url.protocol)) {
|
|
49
|
+
throw new providers_1.ProviderValidationError(`Invalid protocol "${url.protocol}"`, {
|
|
50
|
+
check: 'url',
|
|
51
|
+
details: {
|
|
52
|
+
protocol: url.protocol,
|
|
53
|
+
validProtocols,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
48
56
|
}
|
|
49
57
|
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
50
58
|
const wsUrl = `${wsProtocol}//${url.host}${url.pathname}${constants_1.TRANSFER_PATH}`;
|
|
59
|
+
const validAuthMethods = ['token'];
|
|
51
60
|
// No auth defined, trying public access for transfer
|
|
52
61
|
if (!auth) {
|
|
53
62
|
ws = new ws_1.WebSocket(wsUrl);
|
|
@@ -59,7 +68,13 @@ class RemoteStrapiDestinationProvider {
|
|
|
59
68
|
}
|
|
60
69
|
// Invalid auth method provided
|
|
61
70
|
else {
|
|
62
|
-
throw new
|
|
71
|
+
throw new providers_1.ProviderValidationError('Auth method not implemented', {
|
|
72
|
+
check: 'auth.type',
|
|
73
|
+
details: {
|
|
74
|
+
auth: auth.type,
|
|
75
|
+
validAuthMethods,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
63
78
|
}
|
|
64
79
|
this.ws = ws;
|
|
65
80
|
this.dispatcher = (0, utils_1.createDispatcher)(this.ws);
|
|
@@ -158,9 +173,9 @@ _RemoteStrapiDestinationProvider_instances = new WeakSet(), _RemoteStrapiDestina
|
|
|
158
173
|
return e;
|
|
159
174
|
}
|
|
160
175
|
if (typeof e === 'string') {
|
|
161
|
-
return new
|
|
176
|
+
return new providers_1.ProviderTransferError(e);
|
|
162
177
|
}
|
|
163
|
-
return new
|
|
178
|
+
return new providers_1.ProviderTransferError('Unexpected error');
|
|
164
179
|
}
|
|
165
180
|
return null;
|
|
166
181
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ILink } from '../../../types';
|
|
2
|
-
export declare const createLinkQuery: (strapi: Strapi.Strapi) => () => {
|
|
2
|
+
export declare const createLinkQuery: (strapi: Strapi.Strapi, trx?: any) => () => {
|
|
3
3
|
generateAll: (uid: string) => AsyncGenerator<ILink>;
|
|
4
4
|
generateAllForAttribute: (uid: string, fieldName: string) => AsyncGenerator<ILink>;
|
|
5
5
|
insert: (link: ILink) => Promise<void>;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createLinkQuery = void 0;
|
|
4
4
|
const fp_1 = require("lodash/fp");
|
|
5
5
|
// TODO: Remove any types when we'll have types for DB metadata
|
|
6
|
-
const createLinkQuery = (strapi) => {
|
|
6
|
+
const createLinkQuery = (strapi, trx) => {
|
|
7
7
|
const query = () => {
|
|
8
8
|
const { connection } = strapi.db;
|
|
9
9
|
async function* generateAllForAttribute(uid, fieldName) {
|
|
@@ -23,6 +23,9 @@ const createLinkQuery = (strapi) => {
|
|
|
23
23
|
if (attribute.joinColumn) {
|
|
24
24
|
const joinColumnName = attribute.joinColumn.name;
|
|
25
25
|
const qb = connection.queryBuilder().select('id', joinColumnName).from(metadata.tableName);
|
|
26
|
+
if (trx) {
|
|
27
|
+
qb.transacting(trx);
|
|
28
|
+
}
|
|
26
29
|
// TODO: stream the query to improve performances
|
|
27
30
|
const entries = await qb;
|
|
28
31
|
for (const entry of entries) {
|
|
@@ -77,6 +80,9 @@ const createLinkQuery = (strapi) => {
|
|
|
77
80
|
columns.right.order,
|
|
78
81
|
].filter((column) => !(0, fp_1.isNil)(column));
|
|
79
82
|
qb.select(validColumns);
|
|
83
|
+
if (trx) {
|
|
84
|
+
qb.transacting(trx);
|
|
85
|
+
}
|
|
80
86
|
// TODO: stream the query to improve performances
|
|
81
87
|
const entries = await qb;
|
|
82
88
|
for (const entry of entries) {
|
|
@@ -127,9 +133,13 @@ const createLinkQuery = (strapi) => {
|
|
|
127
133
|
const payload = {};
|
|
128
134
|
if (attribute.joinColumn) {
|
|
129
135
|
const joinColumnName = attribute.joinColumn.name;
|
|
130
|
-
|
|
136
|
+
const qb = connection(metadata.tableName)
|
|
131
137
|
.where('id', left.ref)
|
|
132
138
|
.update({ [joinColumnName]: right.ref });
|
|
139
|
+
if (trx) {
|
|
140
|
+
qb.transacting(trx);
|
|
141
|
+
}
|
|
142
|
+
await qb;
|
|
133
143
|
}
|
|
134
144
|
if (attribute.joinTable) {
|
|
135
145
|
const { name, joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName, morphColumn, } = attribute.joinTable;
|
|
@@ -168,7 +178,11 @@ const createLinkQuery = (strapi) => {
|
|
|
168
178
|
assignMorphColumns();
|
|
169
179
|
}
|
|
170
180
|
assignOrderColumns();
|
|
171
|
-
|
|
181
|
+
const qb = connection.insert(payload).into(name);
|
|
182
|
+
if (trx) {
|
|
183
|
+
qb.transacting(trx);
|
|
184
|
+
}
|
|
185
|
+
await qb;
|
|
172
186
|
}
|
|
173
187
|
};
|
|
174
188
|
return { generateAll, generateAllForAttribute, insert };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TRANSFER_PATH = void 0;
|
|
3
|
+
exports.TRANSFER_METHODS = exports.TRANSFER_PATH = void 0;
|
|
4
4
|
exports.TRANSFER_PATH = '/transfer';
|
|
5
|
+
exports.TRANSFER_METHODS = ['push', 'pull'];
|
|
5
6
|
//# sourceMappingURL=constants.js.map
|
|
@@ -7,6 +7,8 @@ exports.createTransferHandler = void 0;
|
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
8
|
const ws_1 = require("ws");
|
|
9
9
|
const push_1 = __importDefault(require("./controllers/push"));
|
|
10
|
+
const providers_1 = require("../../errors/providers");
|
|
11
|
+
const constants_1 = require("./constants");
|
|
10
12
|
const createTransferHandler = (options = {}) => async (ctx) => {
|
|
11
13
|
const upgradeHeader = (ctx.request.headers.upgrade || '')
|
|
12
14
|
.split(',')
|
|
@@ -54,10 +56,12 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
54
56
|
callback(e);
|
|
55
57
|
}
|
|
56
58
|
else if (typeof e === 'string') {
|
|
57
|
-
callback(new
|
|
59
|
+
callback(new providers_1.ProviderTransferError(e));
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
|
-
callback(new
|
|
62
|
+
callback(new providers_1.ProviderTransferError('Unexpected error', {
|
|
63
|
+
error: e,
|
|
64
|
+
}));
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
};
|
|
@@ -67,8 +71,9 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
67
71
|
return { ok: true };
|
|
68
72
|
};
|
|
69
73
|
const init = (msg) => {
|
|
74
|
+
// TODO: this only checks for this instance of node: we should consider a database lock
|
|
70
75
|
if (state.controller) {
|
|
71
|
-
throw new
|
|
76
|
+
throw new providers_1.ProviderInitializationError('Transfer already in progres');
|
|
72
77
|
}
|
|
73
78
|
const { transfer } = msg.params;
|
|
74
79
|
// Push transfer
|
|
@@ -82,7 +87,10 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
82
87
|
}
|
|
83
88
|
// Pull or any other string
|
|
84
89
|
else {
|
|
85
|
-
throw new
|
|
90
|
+
throw new providers_1.ProviderTransferError(`Transfer type not implemented: "${transfer}"`, {
|
|
91
|
+
transfer,
|
|
92
|
+
validTransfers: constants_1.TRANSFER_METHODS,
|
|
93
|
+
});
|
|
86
94
|
}
|
|
87
95
|
state.transfer = { id: (0, crypto_1.randomUUID)(), kind: transfer };
|
|
88
96
|
return { transferID: state.transfer.id };
|
|
@@ -99,29 +107,35 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
99
107
|
await answer(teardown);
|
|
100
108
|
}
|
|
101
109
|
if (command === 'status') {
|
|
102
|
-
await callback(new
|
|
110
|
+
await callback(new providers_1.ProviderTransferError('Command not implemented: "status"', {
|
|
111
|
+
command,
|
|
112
|
+
validCommands: ['init', 'end', 'status'],
|
|
113
|
+
}));
|
|
103
114
|
}
|
|
104
115
|
};
|
|
105
116
|
const onTransferCommand = async (msg) => {
|
|
106
117
|
const { transferID, kind } = msg;
|
|
107
118
|
const { controller } = state;
|
|
108
119
|
// TODO: (re)move this check
|
|
109
|
-
// It shouldn't be possible to
|
|
120
|
+
// It shouldn't be possible to start a pull transfer for now, so reaching
|
|
110
121
|
// this code should be impossible too, but this has been added by security
|
|
111
122
|
if (state.transfer?.kind === 'pull') {
|
|
112
|
-
return callback(new
|
|
123
|
+
return callback(new providers_1.ProviderTransferError('Pull transfer not implemented'));
|
|
113
124
|
}
|
|
114
125
|
if (!controller) {
|
|
115
|
-
return callback(new
|
|
126
|
+
return callback(new providers_1.ProviderTransferError("The transfer hasn't been initialized"));
|
|
116
127
|
}
|
|
117
128
|
if (!transferID) {
|
|
118
|
-
return callback(new
|
|
129
|
+
return callback(new providers_1.ProviderTransferError('Missing transfer ID'));
|
|
119
130
|
}
|
|
120
131
|
// Action
|
|
121
132
|
if (kind === 'action') {
|
|
122
133
|
const { action } = msg;
|
|
123
134
|
if (!(action in controller.actions)) {
|
|
124
|
-
return callback(new
|
|
135
|
+
return callback(new providers_1.ProviderTransferError(`Invalid action provided: "${action}"`, {
|
|
136
|
+
action,
|
|
137
|
+
validActions: Object.keys(controller.actions),
|
|
138
|
+
}));
|
|
125
139
|
}
|
|
126
140
|
await answer(() => controller.actions[action]());
|
|
127
141
|
}
|
|
@@ -148,12 +162,14 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
148
162
|
});
|
|
149
163
|
ws.on('error', (e) => {
|
|
150
164
|
teardown();
|
|
165
|
+
// TODO: is logging a console error to the running instance of Strapi ok to do? Should we check for an existing strapi.logger to use?
|
|
151
166
|
console.error(e);
|
|
152
167
|
});
|
|
153
168
|
ws.on('message', async (raw) => {
|
|
154
169
|
const msg = JSON.parse(raw.toString());
|
|
155
170
|
if (!msg.uuid) {
|
|
156
|
-
|
|
171
|
+
await callback(new providers_1.ProviderTransferError('Missing uuid in message'));
|
|
172
|
+
return;
|
|
157
173
|
}
|
|
158
174
|
uuid = msg.uuid;
|
|
159
175
|
// Regular command message (init, end, status)
|
|
@@ -166,7 +182,7 @@ const createTransferHandler = (options = {}) => async (ctx) => {
|
|
|
166
182
|
}
|
|
167
183
|
// Invalid messages
|
|
168
184
|
else {
|
|
169
|
-
await callback(new
|
|
185
|
+
await callback(new providers_1.ProviderTransferError('Bad request'));
|
|
170
186
|
}
|
|
171
187
|
});
|
|
172
188
|
});
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
|
@@ -23,9 +23,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.schema = exports.json = exports.stream = exports.encryption = void 0;
|
|
26
|
+
exports.transaction = exports.schema = exports.json = exports.stream = exports.encryption = void 0;
|
|
27
27
|
exports.encryption = __importStar(require("./encryption"));
|
|
28
28
|
exports.stream = __importStar(require("./stream"));
|
|
29
29
|
exports.json = __importStar(require("./json"));
|
|
30
30
|
exports.schema = __importStar(require("./schema"));
|
|
31
|
+
exports.transaction = __importStar(require("./transaction"));
|
|
31
32
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertValidStrapi = void 0;
|
|
4
|
+
const providers_1 = require("../errors/providers");
|
|
5
|
+
const assertValidStrapi = (strapi, msg = '') => {
|
|
6
|
+
if (!strapi) {
|
|
7
|
+
throw new providers_1.ProviderInitializationError(`${msg}. Strapi instance not found.`);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
exports.assertValidStrapi = assertValidStrapi;
|
|
11
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTransaction = void 0;
|
|
4
|
+
const events_1 = require("events");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const createTransaction = (strapi) => {
|
|
7
|
+
const fns = [];
|
|
8
|
+
let done = false;
|
|
9
|
+
let resume = null;
|
|
10
|
+
const e = new events_1.EventEmitter();
|
|
11
|
+
e.on('spawn', (uuid, cb) => {
|
|
12
|
+
fns.push({ fn: cb, uuid });
|
|
13
|
+
resume?.();
|
|
14
|
+
});
|
|
15
|
+
e.on('close', () => {
|
|
16
|
+
done = true;
|
|
17
|
+
resume?.();
|
|
18
|
+
});
|
|
19
|
+
strapi.db.transaction(async ({ trx, rollback }) => {
|
|
20
|
+
e.on('rollback', async () => {
|
|
21
|
+
await rollback();
|
|
22
|
+
});
|
|
23
|
+
while (!done) {
|
|
24
|
+
while (fns.length) {
|
|
25
|
+
const item = fns.shift();
|
|
26
|
+
if (item) {
|
|
27
|
+
const { fn, uuid } = item;
|
|
28
|
+
try {
|
|
29
|
+
const res = await fn(trx);
|
|
30
|
+
e.emit(uuid, { data: res });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
e.emit(uuid, { error });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!done && !fns.length) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
39
|
+
await new Promise((resolve) => {
|
|
40
|
+
resume = resolve;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
async attach(callback) {
|
|
47
|
+
const uuid = (0, crypto_1.randomUUID)();
|
|
48
|
+
e.emit('spawn', uuid, callback);
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
e.on(uuid, ({ data, error }) => {
|
|
51
|
+
if (data) {
|
|
52
|
+
resolve(data);
|
|
53
|
+
}
|
|
54
|
+
if (error) {
|
|
55
|
+
reject(error);
|
|
56
|
+
}
|
|
57
|
+
resolve(undefined);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
end() {
|
|
62
|
+
return e.emit('close');
|
|
63
|
+
},
|
|
64
|
+
rollback() {
|
|
65
|
+
return e.emit('rollback');
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
exports.createTransaction = createTransaction;
|
|
70
|
+
//# sourceMappingURL=transaction.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/data-transfer",
|
|
3
|
-
"version": "4.6.0
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Data transfer capabilities for Strapi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"build": "yarn build:ts",
|
|
31
31
|
"build:ts": "tsc -p tsconfig.json",
|
|
32
32
|
"build:clean": "yarn clean && yarn build",
|
|
33
|
-
"clean": "rimraf ./
|
|
33
|
+
"clean": "rimraf ./lib",
|
|
34
34
|
"prepublishOnly": "yarn build:clean",
|
|
35
35
|
"test:unit": "jest --verbose",
|
|
36
36
|
"watch": "yarn build:ts -w --preserveWatchOutput"
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"lib": "./lib"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@strapi/logger": "4.6.0
|
|
43
|
-
"@strapi/strapi": "4.6.0
|
|
42
|
+
"@strapi/logger": "4.6.0",
|
|
43
|
+
"@strapi/strapi": "4.6.0",
|
|
44
44
|
"chalk": "4.1.2",
|
|
45
45
|
"fs-extra": "10.0.0",
|
|
46
46
|
"lodash": "4.17.21",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"@types/tar": "6.1.3",
|
|
65
65
|
"@types/tar-stream": "2.2.2",
|
|
66
66
|
"@types/uuid": "9.0.0",
|
|
67
|
+
"knex": "2.4.0",
|
|
67
68
|
"koa": "2.13.4",
|
|
68
69
|
"rimraf": "3.0.2",
|
|
69
70
|
"typescript": "4.6.2"
|
|
@@ -72,5 +73,5 @@
|
|
|
72
73
|
"node": ">=14.19.1 <=18.x.x",
|
|
73
74
|
"npm": ">=6.0.0"
|
|
74
75
|
},
|
|
75
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "a9e55435c489f3379d88565bf3f729deb29bfb45"
|
|
76
77
|
}
|