kourou 0.19.4 → 0.20.2
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/README.md +118 -104
- package/lib/commands/app/scaffold.d.ts +2 -0
- package/lib/commands/app/scaffold.js +23 -0
- package/lib/commands/collection/export.d.ts +1 -0
- package/lib/commands/collection/export.js +21 -4
- package/lib/commands/document/search.js +1 -1
- package/lib/commands/role/export.js +0 -1
- package/lib/common.d.ts +19 -0
- package/lib/common.js +55 -12
- package/lib/support/dump-collection.d.ts +1 -1
- package/lib/support/dump-collection.js +207 -57
- package/oclif.manifest.json +1 -1
- package/package.json +19 -18
- package/templates.tgz +0 -0
- package/lib/commands/es/snapshot/restore.d.ts +0 -18
- package/lib/commands/es/snapshot/restore.js +0 -52
|
@@ -6,72 +6,222 @@ const fs_1 = (0, tslib_1.__importDefault)(require("fs"));
|
|
|
6
6
|
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
7
7
|
const cli_ux_1 = (0, tslib_1.__importDefault)(require("cli-ux"));
|
|
8
8
|
const ndjson_1 = (0, tslib_1.__importDefault)(require("ndjson"));
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
const common_1 = require("../common");
|
|
10
|
+
/**
|
|
11
|
+
* Flatten an object transform:
|
|
12
|
+
* {
|
|
13
|
+
* title: "kuzzle",
|
|
14
|
+
* info : {
|
|
15
|
+
* tag: "news"
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* Into an object like:
|
|
20
|
+
* {
|
|
21
|
+
* title: "kuzzle",
|
|
22
|
+
* info.tag: news
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} target the object we have to flatten
|
|
26
|
+
* @returns {Object} the flattened object
|
|
27
|
+
*/
|
|
28
|
+
function flattenObject(target) {
|
|
29
|
+
const output = {};
|
|
30
|
+
flattenStep(output, target);
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
function flattenStep(output, object, prev = null) {
|
|
34
|
+
const keys = Object.keys(object);
|
|
35
|
+
for (let i = 0; i < keys.length; i++) {
|
|
36
|
+
const key = keys[i];
|
|
37
|
+
const value = object[key];
|
|
38
|
+
const newKey = prev ? prev + '.' + key : key;
|
|
39
|
+
if (Object.prototype.toString.call(value) === '[object Object]') {
|
|
40
|
+
output[newKey] = value;
|
|
41
|
+
flattenStep(output, value, newKey);
|
|
42
|
+
}
|
|
43
|
+
output[newKey] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class AbstractDumper {
|
|
47
|
+
constructor(sdk, index, collection, batchSize, destPath, query = {}) {
|
|
48
|
+
this.sdk = sdk;
|
|
49
|
+
this.index = index;
|
|
50
|
+
this.collection = collection;
|
|
51
|
+
this.batchSize = batchSize;
|
|
52
|
+
this.destPath = destPath;
|
|
53
|
+
this.query = query;
|
|
54
|
+
this.collectionDir = path_1.default.join(this.destPath, this.collection);
|
|
55
|
+
this.options = {
|
|
56
|
+
scroll: '2s',
|
|
57
|
+
size: batchSize
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* One-shot call before the dump. Can be used to
|
|
62
|
+
* perform setup operations before dumping.
|
|
63
|
+
*
|
|
64
|
+
* @returns void
|
|
65
|
+
*/
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
67
|
+
async setup() { }
|
|
68
|
+
/**
|
|
69
|
+
* One-shot call before iterating over the data. Can be
|
|
70
|
+
* used to write the header of the dumped output.
|
|
71
|
+
*/
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
73
|
+
async writeHeader() { }
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
75
|
+
async tearDown() { }
|
|
76
|
+
/**
|
|
77
|
+
* The loop that iterates over the documents of the collection and
|
|
78
|
+
* calls all the other hooks.
|
|
79
|
+
*
|
|
80
|
+
* @returns a promise resolving when the dump is finished.
|
|
81
|
+
*/
|
|
82
|
+
async dump() {
|
|
83
|
+
this.filename = path_1.default.join(this.collectionDir, `documents.${this.fileExtension}`);
|
|
84
|
+
this.writeStream = fs_1.default.createWriteStream(this.filename);
|
|
85
|
+
const waitWrite = new Promise((resolve, reject) => this.writeStream ? this.writeStream.on('finish', resolve) : reject());
|
|
86
|
+
this.writeStream.on('error', error => {
|
|
87
|
+
throw error;
|
|
88
|
+
});
|
|
89
|
+
await this.setup();
|
|
90
|
+
await this.writeHeader();
|
|
91
|
+
let results = await this.sdk.document.search(this.index, this.collection, { query: this.query }, this.options);
|
|
92
|
+
const progressBar = cli_ux_1.default.progress({
|
|
93
|
+
format: `Dumping ${this.collection} |{bar}| {percentage}% || {value}/{total} documents`
|
|
94
|
+
});
|
|
95
|
+
progressBar.start(results.total, 0);
|
|
96
|
+
do {
|
|
97
|
+
progressBar.update(results.fetched);
|
|
98
|
+
for (const hit of results.hits) {
|
|
99
|
+
await this.onResult({
|
|
100
|
+
_id: hit._id,
|
|
101
|
+
_source: hit._source
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
} while ((results = await results.next()));
|
|
105
|
+
await this.tearDown();
|
|
106
|
+
progressBar.stop();
|
|
107
|
+
this.writeStream.end();
|
|
108
|
+
return waitWrite;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
class JSONLDumper extends AbstractDumper {
|
|
112
|
+
constructor() {
|
|
113
|
+
super(...arguments);
|
|
114
|
+
this.ndjsonStream = ndjson_1.default.stringify();
|
|
115
|
+
}
|
|
116
|
+
async setup() {
|
|
117
|
+
this.ndjsonStream.on('data', (line) => {
|
|
118
|
+
if (!this.writeStream) {
|
|
119
|
+
throw new Error('Cannot write data: WriteStream is not initialized.');
|
|
120
|
+
}
|
|
121
|
+
this.writeStream.write(line);
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
async writeHeader() {
|
|
126
|
+
await this.writeLine({ type: 'collection', index: this.index, collection: this.collection });
|
|
127
|
+
}
|
|
128
|
+
writeLine(content) {
|
|
20
129
|
return new Promise(resolve => {
|
|
21
|
-
if (ndjsonStream.write(content)) {
|
|
130
|
+
if (this.ndjsonStream.write(content)) {
|
|
22
131
|
resolve(undefined);
|
|
23
132
|
}
|
|
24
133
|
else {
|
|
25
|
-
ndjsonStream.once('drain', resolve);
|
|
134
|
+
this.ndjsonStream.once('drain', resolve);
|
|
26
135
|
}
|
|
27
136
|
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const rawDocuments = {
|
|
43
|
-
[index]: {
|
|
44
|
-
[collection]: []
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
do {
|
|
48
|
-
progressBar.update(results.fetched);
|
|
49
|
-
for (const hit of results.hits) {
|
|
50
|
-
let document = null;
|
|
51
|
-
if (format.toLocaleLowerCase() === 'jsonl') {
|
|
52
|
-
document = {
|
|
53
|
-
_id: hit._id,
|
|
54
|
-
body: hit._source
|
|
55
|
-
};
|
|
56
|
-
await writeLine(document);
|
|
137
|
+
}
|
|
138
|
+
onResult(document) {
|
|
139
|
+
return this.writeLine({ _id: document._id, body: document._source });
|
|
140
|
+
}
|
|
141
|
+
get fileExtension() {
|
|
142
|
+
return 'jsonl';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
class KuzzleDumper extends JSONLDumper {
|
|
146
|
+
constructor() {
|
|
147
|
+
super(...arguments);
|
|
148
|
+
this.rawDocuments = {
|
|
149
|
+
[this.index]: {
|
|
150
|
+
[this.collection]: []
|
|
57
151
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
get fileExtension() {
|
|
155
|
+
return 'json';
|
|
156
|
+
}
|
|
157
|
+
async onResult(document) {
|
|
158
|
+
this.rawDocuments[this.index][this.collection].push({
|
|
159
|
+
index: {
|
|
160
|
+
_id: document._id
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
this.rawDocuments[this.index][this.collection].push(document._source);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
tearDown() {
|
|
167
|
+
return this.writeLine(this.rawDocuments);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
class CSVDumper extends AbstractDumper {
|
|
171
|
+
constructor(sdk, index, collection, batchSize, destPath, query = {}, fields, separator = ',') {
|
|
172
|
+
super(sdk, index, collection, batchSize, destPath, query);
|
|
173
|
+
this.fields = fields;
|
|
174
|
+
this.separator = separator;
|
|
175
|
+
}
|
|
176
|
+
get fileExtension() {
|
|
177
|
+
return 'csv';
|
|
178
|
+
}
|
|
179
|
+
async setup() {
|
|
180
|
+
if (!this.fields.length) {
|
|
181
|
+
// If no field has been selected, then all fields are selected.
|
|
182
|
+
const mappings = await this.sdk.collection.getMapping(this.index, this.collection);
|
|
183
|
+
if (!mappings.properties) {
|
|
184
|
+
return;
|
|
65
185
|
}
|
|
186
|
+
this.fields = Object.keys(flattenObject(mappings.properties));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// Delete '_id' from the selected fields, since IDs are
|
|
190
|
+
// _always_ exported.
|
|
191
|
+
if (this.fields.includes('_id')) {
|
|
192
|
+
this.fields.splice(this.fields.indexOf('_id'), 1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
writeHeader() {
|
|
197
|
+
return this.writeLine(['_id', ...this.fields].join(this.separator));
|
|
198
|
+
}
|
|
199
|
+
async writeLine(line) {
|
|
200
|
+
if (!this.writeStream) {
|
|
201
|
+
throw new Error('Cannot write data: WriteStream is not initialized.');
|
|
66
202
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
203
|
+
this.writeStream.write(line);
|
|
204
|
+
this.writeStream.write('\n');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
onResult(document) {
|
|
208
|
+
const values = [document._id, ...(0, common_1.pickValues)(document._source, this.fields)];
|
|
209
|
+
return this.writeLine(values.join(this.separator));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function dumpCollectionData(sdk, index, collection, batchSize, destPath, query = {}, format = 'jsonl', fields = []) {
|
|
213
|
+
let dumper;
|
|
214
|
+
switch (format.toLowerCase()) {
|
|
215
|
+
case 'jsonl':
|
|
216
|
+
dumper = new JSONLDumper(sdk, index, collection, batchSize, destPath, query);
|
|
217
|
+
return dumper.dump();
|
|
218
|
+
case 'csv':
|
|
219
|
+
dumper = new CSVDumper(sdk, index, collection, batchSize, destPath, query, fields);
|
|
220
|
+
return dumper.dump();
|
|
221
|
+
default:
|
|
222
|
+
dumper = new KuzzleDumper(sdk, index, collection, batchSize, destPath, query);
|
|
223
|
+
return dumper.dump();
|
|
224
|
+
}
|
|
75
225
|
}
|
|
76
226
|
exports.dumpCollectionData = dumpCollectionData;
|
|
77
227
|
async function dumpCollectionMappings(sdk, index, collection, destPath, format = 'jsonl') {
|