@forzalabs/remora 0.0.18 → 0.0.20
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/Constants.js +6 -2
- package/actions/compile.js +1 -1
- package/actions/run.js +23 -9
- package/drivers/DriverFactory.js +39 -1
- package/drivers/LocalDriver.js +70 -2
- package/drivers/S3Driver.js +52 -0
- package/engines/Environment.js +22 -4
- package/engines/ProducerEngine.js +2 -1
- package/engines/Validator.js +1 -2
- package/engines/execution/ExecutionPlanner.js +2 -2
- package/engines/file/FileContentBuilder.js +34 -0
- package/engines/file/FileExporter.js +32 -49
- package/package.json +1 -1
package/Constants.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const CONSTANTS = {
|
|
4
|
-
cliVersion: '0.0.
|
|
4
|
+
cliVersion: '0.0.20',
|
|
5
5
|
lambdaVersion: 1,
|
|
6
|
-
port: 5069
|
|
6
|
+
port: 5069,
|
|
7
|
+
defaults: {
|
|
8
|
+
SQL_MAX_QUERY_ROWS: 10000,
|
|
9
|
+
STRING_MAX_CHARACTERS_LENGTH: 10000000
|
|
10
|
+
}
|
|
7
11
|
};
|
|
8
12
|
exports.default = CONSTANTS;
|
package/actions/compile.js
CHANGED
|
@@ -39,7 +39,7 @@ const compile = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
39
39
|
errors = [...errors, ...envErrors];
|
|
40
40
|
if (errors.length === 0) {
|
|
41
41
|
spinner.succeed(chalk_1.default.green('Project structure validated successfully'));
|
|
42
|
-
console.log(chalk_1.default.
|
|
42
|
+
console.log(chalk_1.default.blueBright.bold('✅ Compilation complete!'));
|
|
43
43
|
}
|
|
44
44
|
else {
|
|
45
45
|
spinner.fail(chalk_1.default.red('Validation failed with errors:'));
|
package/actions/run.js
CHANGED
|
@@ -19,9 +19,11 @@ const Environment_1 = __importDefault(require("../engines/Environment"));
|
|
|
19
19
|
const UserManager_1 = __importDefault(require("../engines/UserManager"));
|
|
20
20
|
const ConsumerEngine_1 = __importDefault(require("../engines/consumer/ConsumerEngine"));
|
|
21
21
|
const compile_1 = require("./compile");
|
|
22
|
+
const Helper_1 = __importDefault(require("../helper/Helper"));
|
|
22
23
|
const run = (consumerName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
24
|
try {
|
|
24
25
|
(0, compile_1.compile)();
|
|
26
|
+
console.log();
|
|
25
27
|
const spinner = (0, ora_1.default)(chalk_1.default.blue('Running consumer(s)...\n')).start();
|
|
26
28
|
const user = UserManager_1.default.getUser();
|
|
27
29
|
const consumersToExecute = (consumerName && consumerName.length > 0)
|
|
@@ -32,26 +34,38 @@ const run = (consumerName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
32
34
|
const consumer = consumersToExecute[i];
|
|
33
35
|
try {
|
|
34
36
|
const response = yield ConsumerEngine_1.default.execute(consumer, {}, user);
|
|
35
|
-
results.push({ consumer, response });
|
|
37
|
+
results.push({ success: true, consumer, response });
|
|
36
38
|
}
|
|
37
39
|
catch (error) {
|
|
38
|
-
|
|
40
|
+
const myErr = Helper_1.default.asError(error);
|
|
41
|
+
results.push({ success: false, consumer, error: myErr.message });
|
|
42
|
+
if (Helper_1.default.isDev())
|
|
43
|
+
console.log(myErr.stack);
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
|
-
spinner.succeed('All consumers have been
|
|
42
|
-
results.forEach(
|
|
43
|
-
if (
|
|
44
|
-
|
|
46
|
+
spinner.succeed('All consumers have been executed:');
|
|
47
|
+
results.forEach(({ response, consumer, success, error }) => {
|
|
48
|
+
if (success) {
|
|
49
|
+
if (response.fileUri)
|
|
50
|
+
console.log(chalk_1.default.green(`• Consumer ${consumer.name} -> ${response.fileUri}`));
|
|
51
|
+
else
|
|
52
|
+
console.log(chalk_1.default.green(`• Consumer ${consumer.name} -> ${response.data.slice(0, 5).join('\n')}`));
|
|
45
53
|
}
|
|
46
54
|
else {
|
|
47
|
-
console.log(chalk_1.default.
|
|
55
|
+
console.log(chalk_1.default.red(`• Consumer ${consumer.name} -> Failed: ${error}`));
|
|
48
56
|
}
|
|
49
57
|
});
|
|
50
|
-
|
|
58
|
+
if (results.some(x => !x.success))
|
|
59
|
+
console.log(chalk_1.default.blueBright('\nℹ️ Run completed with errors'));
|
|
60
|
+
else
|
|
61
|
+
console.log(chalk_1.default.green('\n✅ Run complete!'));
|
|
51
62
|
process.exit(1);
|
|
52
63
|
}
|
|
53
64
|
catch (err) {
|
|
54
|
-
|
|
65
|
+
const myErr = Helper_1.default.asError(err);
|
|
66
|
+
console.error(chalk_1.default.red.bold('\n❌ Unexpected error during run:'), myErr.message);
|
|
67
|
+
if (Helper_1.default.isDev())
|
|
68
|
+
console.log(myErr.stack);
|
|
55
69
|
process.exit(1);
|
|
56
70
|
}
|
|
57
71
|
});
|
package/drivers/DriverFactory.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -12,7 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
45
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
46
|
};
|
|
14
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const LocalDriver_1 =
|
|
48
|
+
const LocalDriver_1 = __importStar(require("./LocalDriver"));
|
|
16
49
|
const RedshiftDriver_1 = __importDefault(require("./RedshiftDriver"));
|
|
17
50
|
const S3Driver_1 = __importDefault(require("./S3Driver"));
|
|
18
51
|
const S3SourceDriver_1 = __importDefault(require("./S3SourceDriver"));
|
|
@@ -47,6 +80,11 @@ class DriverFactoryClass {
|
|
|
47
80
|
yield driver.init(source);
|
|
48
81
|
return driver;
|
|
49
82
|
}
|
|
83
|
+
case 'local': {
|
|
84
|
+
const driver = new LocalDriver_1.LocalDriverDestination();
|
|
85
|
+
yield driver.init(source);
|
|
86
|
+
return driver;
|
|
87
|
+
}
|
|
50
88
|
default: throw new Error(`Invalid driver type "${source.engine}". This driver is not implemented yet`);
|
|
51
89
|
}
|
|
52
90
|
});
|
package/drivers/LocalDriver.js
CHANGED
|
@@ -52,6 +52,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
52
52
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
53
53
|
};
|
|
54
54
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.LocalDriverDestination = void 0;
|
|
55
56
|
const fs = __importStar(require("fs"));
|
|
56
57
|
const path_1 = __importDefault(require("path"));
|
|
57
58
|
const readline_1 = __importDefault(require("readline"));
|
|
@@ -60,9 +61,9 @@ class LocalDriver {
|
|
|
60
61
|
constructor() {
|
|
61
62
|
this.init = (source) => __awaiter(this, void 0, void 0, function* () {
|
|
62
63
|
const fileURL = source.authentication['path'];
|
|
63
|
-
(0, Affirm_1.default)(fileURL,
|
|
64
|
+
(0, Affirm_1.default)(fileURL, `Missing file path in the authentication of source "${source.name}"`);
|
|
64
65
|
const exist = fs.existsSync(fileURL);
|
|
65
|
-
(0, Affirm_1.default)(exist,
|
|
66
|
+
(0, Affirm_1.default)(exist, `The path (${fileURL}) for source "${source.name}" does NOT exist.`);
|
|
66
67
|
this._path = source.authentication['path'];
|
|
67
68
|
return this;
|
|
68
69
|
});
|
|
@@ -119,4 +120,71 @@ class LocalDriver {
|
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
}
|
|
123
|
+
class LocalDriverDestination {
|
|
124
|
+
constructor() {
|
|
125
|
+
this.init = (source) => __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
(0, Affirm_1.default)(source, `Invalid source`);
|
|
127
|
+
const fileURL = source.authentication['path'];
|
|
128
|
+
(0, Affirm_1.default)(fileURL, `Missing file path in the authentication of source "${source.name}"`);
|
|
129
|
+
const exist = fs.existsSync(fileURL);
|
|
130
|
+
(0, Affirm_1.default)(exist, `The path (${fileURL}) for source "${source.name}" does NOT exist.`);
|
|
131
|
+
this._path = source.authentication['path'];
|
|
132
|
+
return this;
|
|
133
|
+
});
|
|
134
|
+
this.uploadFile = (options) => __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
(0, Affirm_1.default)(this._path, 'Path not initialized');
|
|
136
|
+
(0, Affirm_1.default)(options, 'Invalid upload options');
|
|
137
|
+
(0, Affirm_1.default)(options.name, 'File name is required');
|
|
138
|
+
(0, Affirm_1.default)(options.content != null, 'File content is required');
|
|
139
|
+
const folder = this._path;
|
|
140
|
+
try {
|
|
141
|
+
if (!fs.existsSync(folder))
|
|
142
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
143
|
+
const filePath = path_1.default.join(folder, options.name);
|
|
144
|
+
fs.writeFileSync(filePath, options.content);
|
|
145
|
+
return { bucket: folder, key: filePath, res: true };
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new Error(`Failed to upload local file "${options.name}": ${error.message}`);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
this.multipartUpload = (options) => __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
(0, Affirm_1.default)(this._path, 'Path not initialized');
|
|
153
|
+
(0, Affirm_1.default)(options, 'Invalid upload options');
|
|
154
|
+
(0, Affirm_1.default)(options.name, 'File name is required');
|
|
155
|
+
(0, Affirm_1.default)(options.contents && Array.isArray(options.contents), 'Contents must be an array');
|
|
156
|
+
(0, Affirm_1.default)(options.contents.length > 0, 'Contents array cannot be empty');
|
|
157
|
+
const folder = this._path;
|
|
158
|
+
try {
|
|
159
|
+
if (!fs.existsSync(folder)) {
|
|
160
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
const filePath = path_1.default.join(folder, options.name);
|
|
163
|
+
// Create or truncate the file first
|
|
164
|
+
fs.writeFileSync(filePath, '');
|
|
165
|
+
// Append each chunk
|
|
166
|
+
for (const chunk of options.contents) {
|
|
167
|
+
(0, Affirm_1.default)(typeof chunk === 'string', 'Each chunk must be a string');
|
|
168
|
+
fs.appendFileSync(filePath, chunk);
|
|
169
|
+
}
|
|
170
|
+
return { bucket: folder, key: filePath, res: true };
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
// Clean up the partial file if it exists
|
|
174
|
+
const filePath = path_1.default.join(folder, options.name);
|
|
175
|
+
if (fs.existsSync(filePath)) {
|
|
176
|
+
try {
|
|
177
|
+
fs.unlinkSync(filePath);
|
|
178
|
+
}
|
|
179
|
+
catch (cleanupError) {
|
|
180
|
+
console.error(`Failed to clean up partial file after error: ${cleanupError.message}`);
|
|
181
|
+
throw cleanupError;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Failed to complete local multipart upload for "${options.name}": ${error.message}`);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.LocalDriverDestination = LocalDriverDestination;
|
|
122
190
|
exports.default = LocalDriver;
|
package/drivers/S3Driver.js
CHANGED
|
@@ -45,6 +45,58 @@ class S3Driver {
|
|
|
45
45
|
(0, Affirm_1.default)(res.$metadata.httpStatusCode === 200, `Failed to upload the file "${name}" to the bucket "${this._bucketName}": status code ${res.$metadata.httpStatusCode}`);
|
|
46
46
|
return { res: true, key: name, bucket: this._bucketName };
|
|
47
47
|
});
|
|
48
|
+
this.multipartUpload = (options) => __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
(0, Affirm_1.default)(options, `Invalid upload options`);
|
|
50
|
+
(0, Affirm_1.default)(options.contents && options.contents.length > 0, 'No contents provided for multipart upload');
|
|
51
|
+
(0, Affirm_1.default)(options.name, 'No filename provided for multipart upload');
|
|
52
|
+
try {
|
|
53
|
+
// Create the multipart upload
|
|
54
|
+
const createMultipartUploadRes = yield this._client.send(new client_s3_1.CreateMultipartUploadCommand({
|
|
55
|
+
Bucket: this._bucketName,
|
|
56
|
+
Key: options.name
|
|
57
|
+
}));
|
|
58
|
+
const uploadId = createMultipartUploadRes.UploadId;
|
|
59
|
+
(0, Affirm_1.default)(uploadId, 'Failed to initiate multipart upload');
|
|
60
|
+
// Upload each part
|
|
61
|
+
const uploadPromises = options.contents.map((content, index) => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
const partNumber = index + 1;
|
|
63
|
+
const uploadPartRes = yield this._client.send(new client_s3_1.UploadPartCommand({
|
|
64
|
+
Bucket: this._bucketName,
|
|
65
|
+
Key: options.name,
|
|
66
|
+
UploadId: uploadId,
|
|
67
|
+
PartNumber: partNumber,
|
|
68
|
+
Body: Buffer.from(content)
|
|
69
|
+
}));
|
|
70
|
+
return {
|
|
71
|
+
PartNumber: partNumber,
|
|
72
|
+
ETag: uploadPartRes.ETag
|
|
73
|
+
};
|
|
74
|
+
}));
|
|
75
|
+
const uploadedParts = yield Promise.all(uploadPromises);
|
|
76
|
+
// Complete the multipart upload
|
|
77
|
+
const completeRes = yield this._client.send(new client_s3_1.CompleteMultipartUploadCommand({
|
|
78
|
+
Bucket: this._bucketName,
|
|
79
|
+
Key: options.name,
|
|
80
|
+
UploadId: uploadId,
|
|
81
|
+
MultipartUpload: {
|
|
82
|
+
Parts: uploadedParts
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
(0, Affirm_1.default)(completeRes.$metadata.httpStatusCode === 200, `Failed to complete multipart upload for "${options.name}": status code ${completeRes.$metadata.httpStatusCode}`);
|
|
86
|
+
return { res: true, key: options.name, bucket: this._bucketName };
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// If anything fails, make sure to abort the multipart upload
|
|
90
|
+
if (error.UploadId) {
|
|
91
|
+
yield this._client.send(new client_s3_1.AbortMultipartUploadCommand({
|
|
92
|
+
Bucket: this._bucketName,
|
|
93
|
+
Key: options.name,
|
|
94
|
+
UploadId: error.UploadId
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
48
100
|
}
|
|
49
101
|
}
|
|
50
102
|
exports.default = S3Driver;
|
package/engines/Environment.js
CHANGED
|
@@ -8,6 +8,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
8
8
|
const Affirm_1 = __importDefault(require("../core/Affirm"));
|
|
9
9
|
const SchemaValidator_1 = __importDefault(require("./schema/SchemaValidator"));
|
|
10
10
|
const Validator_1 = __importDefault(require("./Validator"));
|
|
11
|
+
const Constants_1 = __importDefault(require("../Constants"));
|
|
11
12
|
class EnvironmentClass {
|
|
12
13
|
constructor() {
|
|
13
14
|
this._env = null;
|
|
@@ -71,9 +72,16 @@ class EnvironmentClass {
|
|
|
71
72
|
if (!SchemaValidator_1.default.validate('consumer-schema', consumer))
|
|
72
73
|
throw new Error(`Invalid consumer configuration: ${consumer.name}`);
|
|
73
74
|
});
|
|
75
|
+
// Load the project settings
|
|
76
|
+
const envSettings = new Map(Object.entries(Object.assign({}, projectConfig.settings))
|
|
77
|
+
.map(([key, value]) => [key, String(value)]));
|
|
78
|
+
if (!envSettings.has('SQL_MAX_QUERY_ROWS'))
|
|
79
|
+
envSettings.set('SQL_MAX_QUERY_ROWS', Constants_1.default.defaults.SQL_MAX_QUERY_ROWS.toString());
|
|
80
|
+
if (!envSettings.has('STRING_MAX_CHARACTERS_LENGTH'))
|
|
81
|
+
envSettings.set('STRING_MAX_CHARACTERS_LENGTH', Constants_1.default.defaults.STRING_MAX_CHARACTERS_LENGTH.toString());
|
|
74
82
|
// Initialize environment
|
|
75
83
|
this.init({
|
|
76
|
-
settings:
|
|
84
|
+
settings: envSettings,
|
|
77
85
|
sources,
|
|
78
86
|
producers,
|
|
79
87
|
consumers,
|
|
@@ -93,7 +101,7 @@ class EnvironmentClass {
|
|
|
93
101
|
*/
|
|
94
102
|
this.getFirstProducer = (producerName) => {
|
|
95
103
|
(0, Affirm_1.default)(producerName, `Invalid producer name`);
|
|
96
|
-
const prod = this._env.producers.find(x => x.name === producerName);
|
|
104
|
+
const prod = this._env.producers.find(x => x.name.toLocaleLowerCase() === producerName.toLowerCase());
|
|
97
105
|
if (!prod) {
|
|
98
106
|
const consumer = this.getConsumer(producerName);
|
|
99
107
|
(0, Affirm_1.default)(consumer, `Invalid producer name`);
|
|
@@ -103,11 +111,11 @@ class EnvironmentClass {
|
|
|
103
111
|
};
|
|
104
112
|
this.getProducer = (producerName) => {
|
|
105
113
|
(0, Affirm_1.default)(producerName, `Invalid producer name`);
|
|
106
|
-
return this._env.producers.find(x => x.name === producerName);
|
|
114
|
+
return this._env.producers.find(x => x.name.toLowerCase() === producerName.toLowerCase());
|
|
107
115
|
};
|
|
108
116
|
this.getConsumer = (consumerName) => {
|
|
109
117
|
(0, Affirm_1.default)(consumerName, `Invalid consumer name`);
|
|
110
|
-
return this._env.consumers.find(x => x.name === consumerName);
|
|
118
|
+
return this._env.consumers.find(x => x.name.toLowerCase() === consumerName.toLowerCase());
|
|
111
119
|
};
|
|
112
120
|
this.getSchema = (schemaName) => {
|
|
113
121
|
(0, Affirm_1.default)(schemaName, 'Invalid schema name');
|
|
@@ -123,7 +131,17 @@ class EnvironmentClass {
|
|
|
123
131
|
try {
|
|
124
132
|
errors = [...errors, ...Validator_1.default.validateSources(this._env.sources)];
|
|
125
133
|
errors = [...errors, ...Validator_1.default.validateProducers(this._env.producers)];
|
|
134
|
+
this._env.producers.forEach(prod => {
|
|
135
|
+
const ce = Validator_1.default.validateProducer(prod);
|
|
136
|
+
if (ce.length > 0)
|
|
137
|
+
errors.push(`Producer "${prod.name}" errors:\n${ce.map(x => `\t-${x}\n`)}`);
|
|
138
|
+
});
|
|
126
139
|
errors = [...errors, ...Validator_1.default.validateConsumers(this._env.consumers)];
|
|
140
|
+
this._env.consumers.forEach(cons => {
|
|
141
|
+
const ce = Validator_1.default.validateConsumer(cons);
|
|
142
|
+
if (ce.length > 0)
|
|
143
|
+
errors.push(`Consumer "${cons.name}" errors:\n${ce.map(x => `\t-${x}\n`)}`);
|
|
144
|
+
});
|
|
127
145
|
}
|
|
128
146
|
catch (e) {
|
|
129
147
|
if (errors.length === 0)
|
|
@@ -82,6 +82,7 @@ class ProducerEngineClass {
|
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
this.readFile = (producer, options) => __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
var _a;
|
|
85
86
|
(0, Affirm_1.default)(producer, 'Invalid producer');
|
|
86
87
|
(0, Affirm_1.default)(options, 'Invalid options');
|
|
87
88
|
if (options.readmode === 'lines')
|
|
@@ -95,7 +96,7 @@ class ProducerEngineClass {
|
|
|
95
96
|
lines = yield driver.readLinesInRange({ fileKey: producer.settings.fileKey, lineFrom: options.lines.from, lineTo: options.lines.to });
|
|
96
97
|
else
|
|
97
98
|
lines = [(yield driver.download({ fileKey: producer.settings.fileKey }))];
|
|
98
|
-
switch (producer.settings.fileType.toUpperCase()) {
|
|
99
|
+
switch ((_a = producer.settings.fileType) === null || _a === void 0 ? void 0 : _a.toUpperCase()) {
|
|
99
100
|
case 'CSV': {
|
|
100
101
|
return { data: lines, dataType: 'lines-of-text' };
|
|
101
102
|
}
|
package/engines/Validator.js
CHANGED
|
@@ -65,7 +65,6 @@ class ValidatorClass {
|
|
|
65
65
|
const dupes = Algo_1.default.duplicatesObject(consumers, 'name');
|
|
66
66
|
if (dupes.length > 0)
|
|
67
67
|
errors.push(`Duplicate name(s) found in consumers: "${dupes.map(x => x.name).join(', ')}"`);
|
|
68
|
-
errors.push(...consumers.flatMap(x => this.validateConsumer(x)));
|
|
69
68
|
}
|
|
70
69
|
catch (e) {
|
|
71
70
|
if (errors.length === 0)
|
|
@@ -121,7 +120,7 @@ class ValidatorClass {
|
|
|
121
120
|
const groupingFields = fields.filter(x => x.grouping);
|
|
122
121
|
if (groupingFields.length > 1)
|
|
123
122
|
errors.push(`There can't be 2 fields with grouping defined at the same level (${groupingFields.map(x => x.key).join(', ')}). Level: ${level}`);
|
|
124
|
-
groupingFields.forEach(field => {
|
|
123
|
+
groupingFields.forEach((field) => {
|
|
125
124
|
if (field.grouping)
|
|
126
125
|
errors = [...errors, ...validateGroupingLevels(field.grouping.subFields, level + 1)];
|
|
127
126
|
});
|
|
@@ -18,7 +18,7 @@ class ExecutionPlannerClas {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
this.plan = (consumer, options) => {
|
|
21
|
-
var _a;
|
|
21
|
+
var _a, _b;
|
|
22
22
|
(0, Affirm_1.default)(consumer, 'Invalid consumer');
|
|
23
23
|
const [source, producer] = ConsumerManager_1.default.getSource(consumer);
|
|
24
24
|
const producerEngine = source.engine;
|
|
@@ -39,7 +39,7 @@ class ExecutionPlannerClas {
|
|
|
39
39
|
plan.push({ type: 'read-file-lines', producer: prod, lines: { from: (_a = options.offset) !== null && _a !== void 0 ? _a : 0, to: options.limit ? (options.offset + options.limit) : undefined } });
|
|
40
40
|
else
|
|
41
41
|
plan.push({ type: 'read-file-whole', producer: prod });
|
|
42
|
-
if (prod.settings.fileType.toUpperCase() === 'CSV') {
|
|
42
|
+
if (((_b = prod.settings.fileType) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'CSV') {
|
|
43
43
|
plan.push({ type: 'csv-to-json', producer: prod });
|
|
44
44
|
}
|
|
45
45
|
if (prod.dimensions.some(x => { var _a, _b; return ((_a = x.alias) === null || _a === void 0 ? void 0 : _a.includes('{')) || ((_b = x.alias) === null || _b === void 0 ? void 0 : _b.includes('[')); }))
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const Affirm_1 = __importDefault(require("../../core/Affirm"));
|
|
7
|
+
const Environment_1 = __importDefault(require("../Environment"));
|
|
8
|
+
class FileContentBuilderClass {
|
|
9
|
+
constructor() {
|
|
10
|
+
/**
|
|
11
|
+
* Converts an array of string to a single string separated with the separator.
|
|
12
|
+
* In the V8 engine there is a maximum length to a string so I can't just join it all.
|
|
13
|
+
* I use this to create chunks that are not too long.
|
|
14
|
+
*/
|
|
15
|
+
this.compose = (lines, separator) => {
|
|
16
|
+
Affirm_1.default.hasValue(lines, 'Invalid lines');
|
|
17
|
+
Affirm_1.default.hasValue(lines, 'Invalid separator');
|
|
18
|
+
const maxStringLength = parseInt(Environment_1.default.get('STRING_MAX_CHARACTERS_LENGTH'));
|
|
19
|
+
const chunks = [];
|
|
20
|
+
let currentChunk = '';
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
currentChunk += (line + separator);
|
|
23
|
+
if (currentChunk.length >= maxStringLength) {
|
|
24
|
+
chunks.push(currentChunk);
|
|
25
|
+
currentChunk = '';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
chunks.push(currentChunk);
|
|
29
|
+
return chunks;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const FileContentBuilder = new FileContentBuilderClass();
|
|
34
|
+
exports.default = FileContentBuilder;
|
|
@@ -1,37 +1,4 @@
|
|
|
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 () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -50,14 +17,22 @@ const Algo_1 = __importDefault(require("../../core/Algo"));
|
|
|
50
17
|
const DSTE_1 = __importDefault(require("../../core/dste/DSTE"));
|
|
51
18
|
const DriverFactory_1 = __importDefault(require("../../drivers/DriverFactory"));
|
|
52
19
|
const Environment_1 = __importDefault(require("../Environment"));
|
|
53
|
-
const
|
|
20
|
+
const FileContentBuilder_1 = __importDefault(require("./FileContentBuilder"));
|
|
54
21
|
class FileExporterClass {
|
|
55
22
|
constructor() {
|
|
56
23
|
this.export = (consumer, output, data) => __awaiter(this, void 0, void 0, function* () {
|
|
57
24
|
(0, Affirm_1.default)(consumer, `Invalid consumer`);
|
|
58
25
|
(0, Affirm_1.default)(output, `Invalid output`);
|
|
59
26
|
(0, Affirm_1.default)(data, `Invalid export data`);
|
|
60
|
-
|
|
27
|
+
const preparedData = this._prepareData(consumer, output, data);
|
|
28
|
+
const writeRes = yield this._writeData(consumer, output, preparedData);
|
|
29
|
+
return writeRes;
|
|
30
|
+
});
|
|
31
|
+
this._prepareData = (consumer, output, data) => {
|
|
32
|
+
(0, Affirm_1.default)(consumer, `Invalid consumer`);
|
|
33
|
+
(0, Affirm_1.default)(output, `Invalid output`);
|
|
34
|
+
(0, Affirm_1.default)(data, `Invalid export data`);
|
|
35
|
+
let exportDataChunks = null;
|
|
61
36
|
let extension = null;
|
|
62
37
|
// build the actual file in the requested format
|
|
63
38
|
switch (output.format.toUpperCase()) {
|
|
@@ -71,8 +46,7 @@ class FileExporterClass {
|
|
|
71
46
|
const rowValues = columns.map(c => { var _a; return Algo_1.default.replaceAll((_a = c === null || c === void 0 ? void 0 : c.toString()) !== null && _a !== void 0 ? _a : '', '"', '""'); }).map(c => `"${c}"`).join(',');
|
|
72
47
|
lines.push(rowValues);
|
|
73
48
|
}
|
|
74
|
-
|
|
75
|
-
exportData = csv;
|
|
49
|
+
exportDataChunks = FileContentBuilder_1.default.compose(lines, '\n');
|
|
76
50
|
extension = 'csv';
|
|
77
51
|
break;
|
|
78
52
|
}
|
|
@@ -82,8 +56,7 @@ class FileExporterClass {
|
|
|
82
56
|
const row = data[i];
|
|
83
57
|
lines.push(JSON.stringify(row));
|
|
84
58
|
}
|
|
85
|
-
|
|
86
|
-
exportData = jsonl;
|
|
59
|
+
exportDataChunks = FileContentBuilder_1.default.compose(lines, '\n');
|
|
87
60
|
extension = 'jsonl';
|
|
88
61
|
break;
|
|
89
62
|
}
|
|
@@ -91,27 +64,37 @@ class FileExporterClass {
|
|
|
91
64
|
throw new Error(`Consumer output "${output.format}" not implemented yet`);
|
|
92
65
|
}
|
|
93
66
|
}
|
|
67
|
+
return { textChunks: exportDataChunks, extension };
|
|
68
|
+
};
|
|
69
|
+
this._writeData = (consumer, output, preparedData) => __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
(0, Affirm_1.default)(consumer, `Invalid consumer`);
|
|
71
|
+
(0, Affirm_1.default)(output, `Invalid output`);
|
|
72
|
+
(0, Affirm_1.default)(preparedData, `Invalid prepared data`);
|
|
94
73
|
// export it where it needs to go
|
|
95
74
|
const source = Environment_1.default.getSource(output.exportDestination);
|
|
96
75
|
(0, Affirm_1.default)(source, `Invalid consumer "${consumer.name}" export location source. Make sure that the export location is an available source.`);
|
|
76
|
+
const { extension, textChunks } = preparedData;
|
|
77
|
+
const name = this._composeFileName(consumer, extension);
|
|
97
78
|
switch (source.engine) {
|
|
98
79
|
case 'local': {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const date = DSTE_1.default.now().toISOString().split('.')[0];
|
|
103
|
-
const path = `${folder}/${consumer.name}_${Algo_1.default.replaceAll(date, ':', '-')}.${extension}`;
|
|
104
|
-
fs.writeFileSync(path, exportData);
|
|
105
|
-
return path;
|
|
80
|
+
const driver = yield DriverFactory_1.default.instantiateDestination(source);
|
|
81
|
+
const res = yield driver.multipartUpload({ contents: textChunks, name });
|
|
82
|
+
return res.key;
|
|
106
83
|
}
|
|
107
84
|
case 'aws-s3': {
|
|
108
85
|
const driver = yield DriverFactory_1.default.instantiateDestination(source);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
86
|
+
if (textChunks.length === 1) {
|
|
87
|
+
const res = yield driver.uploadFile({ content: textChunks[0], name });
|
|
88
|
+
return res.key;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const res = yield driver.multipartUpload({ contents: textChunks, name });
|
|
92
|
+
return res.key;
|
|
93
|
+
}
|
|
112
94
|
}
|
|
113
95
|
}
|
|
114
96
|
});
|
|
97
|
+
this._composeFileName = (consumer, extension) => `${consumer.name}_${Algo_1.default.replaceAll(DSTE_1.default.now().toISOString().split('.')[0], ':', '-')}.${extension}`;
|
|
115
98
|
}
|
|
116
99
|
}
|
|
117
100
|
const FileExporter = new FileExporterClass();
|