@forzalabs/remora 1.0.21 → 1.1.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 (111) hide show
  1. package/actions/automap.js +26 -42
  2. package/actions/compile.js +27 -43
  3. package/actions/create_consumer.js +24 -40
  4. package/actions/create_producer.js +16 -32
  5. package/actions/debug.js +18 -34
  6. package/actions/deploy.js +30 -46
  7. package/actions/discover.js +13 -29
  8. package/actions/init.js +29 -45
  9. package/actions/mock.js +16 -32
  10. package/actions/run.js +34 -52
  11. package/actions/sample.js +42 -58
  12. package/index.js +38 -43
  13. package/package.json +4 -4
  14. package/workers/ExecutorWorker.js +18 -32
  15. package/Constants.js +0 -34
  16. package/core/Affirm.js +0 -42
  17. package/core/Algo.js +0 -160
  18. package/core/dste/DSTE.js +0 -113
  19. package/core/logger/DebugLogService.js +0 -48
  20. package/core/logger/DevelopmentLogService.js +0 -70
  21. package/core/logger/LocalLogService.js +0 -70
  22. package/core/logger/Logger.js +0 -54
  23. package/database/DatabaseEngine.js +0 -149
  24. package/database/DatabaseStructure.js +0 -27
  25. package/definitions/DatasetDefinitions.js +0 -2
  26. package/definitions/ExecutorDefinitions.js +0 -2
  27. package/definitions/ProcessENV.js +0 -2
  28. package/definitions/agents/DestinationDriver.js +0 -2
  29. package/definitions/agents/SourceDriver.js +0 -2
  30. package/definitions/cli.js +0 -2
  31. package/definitions/database/ApiKeys.js +0 -2
  32. package/definitions/database/Stored.js +0 -7
  33. package/definitions/database/UsageStat.js +0 -2
  34. package/definitions/database/User.js +0 -2
  35. package/definitions/json_schemas/consumer-schema.json +0 -1226
  36. package/definitions/json_schemas/producer-schema.json +0 -308
  37. package/definitions/json_schemas/project-schema.json +0 -100
  38. package/definitions/json_schemas/source-schema.json +0 -249
  39. package/definitions/requests/ConsumerRequest.js +0 -2
  40. package/definitions/requests/Developer.js +0 -2
  41. package/definitions/requests/Mapping.js +0 -2
  42. package/definitions/requests/ProducerRequest.js +0 -2
  43. package/definitions/requests/Request.js +0 -2
  44. package/definitions/resources/Compiled.js +0 -2
  45. package/definitions/resources/Consumer.js +0 -2
  46. package/definitions/resources/Environment.js +0 -2
  47. package/definitions/resources/Library.js +0 -2
  48. package/definitions/resources/Producer.js +0 -2
  49. package/definitions/resources/Project.js +0 -2
  50. package/definitions/resources/Schema.js +0 -2
  51. package/definitions/resources/Source.js +0 -2
  52. package/definitions/temp.js +0 -2
  53. package/definitions/transform/Transformations.js +0 -2
  54. package/drivers/DeltaShareDriver.js +0 -186
  55. package/drivers/DriverFactory.js +0 -72
  56. package/drivers/DriverHelper.js +0 -248
  57. package/drivers/HttpApiDriver.js +0 -208
  58. package/drivers/RedshiftDriver.js +0 -184
  59. package/drivers/files/LocalDestinationDriver.js +0 -146
  60. package/drivers/files/LocalSourceDriver.js +0 -405
  61. package/drivers/s3/S3DestinationDriver.js +0 -197
  62. package/drivers/s3/S3SourceDriver.js +0 -495
  63. package/engines/CryptoEngine.js +0 -75
  64. package/engines/Environment.js +0 -170
  65. package/engines/ProcessENVManager.js +0 -83
  66. package/engines/RandomEngine.js +0 -47
  67. package/engines/SecretManager.js +0 -23
  68. package/engines/UserManager.js +0 -66
  69. package/engines/ai/AutoMapperEngine.js +0 -37
  70. package/engines/ai/DeveloperEngine.js +0 -497
  71. package/engines/ai/LLM.js +0 -255
  72. package/engines/consumer/ConsumerManager.js +0 -218
  73. package/engines/consumer/ConsumerOnFinishManager.js +0 -202
  74. package/engines/dataset/Dataset.js +0 -824
  75. package/engines/dataset/DatasetManager.js +0 -211
  76. package/engines/dataset/DatasetRecord.js +0 -120
  77. package/engines/dataset/DatasetRecordPool.js +0 -77
  78. package/engines/execution/RequestExecutor.js +0 -67
  79. package/engines/parsing/CSVParser.js +0 -60
  80. package/engines/parsing/LineParser.js +0 -71
  81. package/engines/parsing/ParseCompression.js +0 -101
  82. package/engines/parsing/ParseHelper.js +0 -18
  83. package/engines/parsing/ParseManager.js +0 -54
  84. package/engines/parsing/XLSParser.js +0 -87
  85. package/engines/parsing/XMLParser.js +0 -115
  86. package/engines/producer/ProducerEngine.js +0 -127
  87. package/engines/producer/ProducerManager.js +0 -43
  88. package/engines/scheduler/CronScheduler.js +0 -222
  89. package/engines/scheduler/QueueManager.js +0 -314
  90. package/engines/schema/SchemaValidator.js +0 -67
  91. package/engines/transform/JoinEngine.js +0 -232
  92. package/engines/transform/TransformationEngine.js +0 -277
  93. package/engines/transform/TypeCaster.js +0 -59
  94. package/engines/usage/DataframeManager.js +0 -55
  95. package/engines/usage/UsageDataManager.js +0 -151
  96. package/engines/usage/UsageManager.js +0 -65
  97. package/engines/validation/Validator.js +0 -216
  98. package/executors/ConsumerExecutor.js +0 -280
  99. package/executors/Executor.js +0 -177
  100. package/executors/ExecutorOrchestrator.js +0 -331
  101. package/executors/ExecutorPerformance.js +0 -17
  102. package/executors/ExecutorProgress.js +0 -54
  103. package/executors/ExecutorScope.js +0 -52
  104. package/executors/OutputExecutor.js +0 -118
  105. package/executors/ProducerExecutor.js +0 -108
  106. package/helper/Helper.js +0 -149
  107. package/helper/Logger.js +0 -84
  108. package/helper/Runtime.js +0 -20
  109. package/helper/Settings.js +0 -13
  110. package/licencing/LicenceManager.js +0 -64
  111. package/settings.js +0 -12
@@ -1,151 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const DSTE_1 = __importDefault(require("../../core/dste/DSTE"));
16
- const DatabaseEngine_1 = __importDefault(require("../../database/DatabaseEngine"));
17
- const DataframeManager_1 = __importDefault(require("./DataframeManager"));
18
- class UsageDataManager {
19
- getUsageDetails() {
20
- return __awaiter(this, void 0, void 0, function* () {
21
- const now = DSTE_1.default.now();
22
- const from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
23
- const prevMonthFrom = new Date(now.getTime() - 60 * 24 * 60 * 60 * 1000);
24
- const yearAgo = new Date(now.getFullYear(), now.getMonth() - 11, 1);
25
- const collection = 'usage';
26
- // Aggregate status counts for current and previous month
27
- const getStatusCounts = (start, end) => __awaiter(this, void 0, void 0, function* () {
28
- const results = yield DatabaseEngine_1.default.aggregate(collection, [
29
- { $match: { startedAt: { $gte: start, $lte: end } } },
30
- { $group: { _id: '$status', count: { $sum: 1 } } }
31
- ]);
32
- let success = 0, failed = 0, total = 0;
33
- results.forEach(r => {
34
- total += r.count;
35
- if (r._id === 'success')
36
- success = r.count;
37
- if (r._id === 'failed')
38
- failed = r.count;
39
- });
40
- return { total, success, failed };
41
- });
42
- const statusesRequests = yield getStatusCounts(from, now);
43
- const prevStatusesRequests = yield getStatusCounts(prevMonthFrom, from);
44
- // Monthly success and fails for last 12 months
45
- const monthlySuccessPipeline = [
46
- { $match: { status: 'success', startedAt: { $gte: yearAgo, $lte: now } } },
47
- { $addFields: { year: { $year: '$startedAt' }, month: { $month: '$startedAt' } } },
48
- { $group: { _id: { year: '$year', month: '$month' }, count: { $sum: 1 } } },
49
- { $project: { _id: 0, x: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] }, y: '$count' } },
50
- { $sort: { x: 1 } }
51
- ];
52
- const monthlyFailsPipeline = [
53
- { $match: { status: 'failed', startedAt: { $gte: yearAgo, $lte: now } } },
54
- { $addFields: { year: { $year: '$startedAt' }, month: { $month: '$startedAt' } } },
55
- { $group: { _id: { year: '$year', month: '$month' }, count: { $sum: 1 } } },
56
- { $project: { _id: 0, x: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] }, y: '$count' } },
57
- { $sort: { x: 1 } }
58
- ];
59
- const rawMonthlySuccess = yield DatabaseEngine_1.default.aggregate(collection, monthlySuccessPipeline);
60
- const rawMonthlyFails = yield DatabaseEngine_1.default.aggregate(collection, monthlyFailsPipeline);
61
- // Top lines per month for last 12 months
62
- const topLinesPipeline = [
63
- { $match: { startedAt: { $gte: yearAgo, $lte: now } } },
64
- { $addFields: { year: { $year: '$startedAt' }, month: { $month: '$startedAt' } } },
65
- { $group: { _id: { year: '$year', month: '$month' }, itemsCount: { $max: '$itemsCount' } } },
66
- { $project: { _id: 0, x: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] }, y: '$itemsCount' } },
67
- { $sort: { x: 1 } }
68
- ];
69
- const topLines = yield DatabaseEngine_1.default.aggregate(collection, topLinesPipeline);
70
- // Top times per month for last 12 months
71
- const topTimePipeline = [
72
- { $match: { startedAt: { $gte: yearAgo, $lte: now } } },
73
- { $addFields: { durationMs: { $subtract: ['$finishedAt', '$startedAt'] }, year: { $year: '$startedAt' }, month: { $month: '$startedAt' } } },
74
- { $group: { _id: { year: '$year', month: '$month' }, maxDuration: { $max: '$durationMs' } } },
75
- { $project: { _id: 0, x: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] }, y: '$maxDuration' } },
76
- { $sort: { x: 1 } }
77
- ];
78
- const topTime = yield DatabaseEngine_1.default.aggregate(collection, topTimePipeline);
79
- // Monthly consumers: for each consumer, per month count
80
- const consumerPipeline = [
81
- { $match: { startedAt: { $gte: yearAgo, $lte: now } } },
82
- { $addFields: { year: { $year: '$startedAt' }, month: { $month: '$startedAt' } } },
83
- { $group: { _id: { consumer: '$consumer', year: '$year', month: '$month' }, count: { $sum: 1 } } },
84
- { $project: { _id: 0, consumer: '$_id.consumer', x: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] }, y: '$count' } },
85
- { $sort: { consumer: 1, x: 1 } }
86
- ];
87
- const consumersData = yield DatabaseEngine_1.default.aggregate(collection, consumerPipeline);
88
- // transform to consumer array
89
- const consumerMap = {};
90
- consumersData.forEach(r => {
91
- consumerMap[r.consumer] = consumerMap[r.consumer] || [];
92
- consumerMap[r.consumer].push({ x: r.x, y: r.y });
93
- });
94
- const consumers = Object.entries(consumerMap).map(([name, data]) => ({ name, data: DataframeManager_1.default.fill(data !== null && data !== void 0 ? data : [], yearAgo, now) }));
95
- // Recent executions
96
- const recentExecution = yield DatabaseEngine_1.default.query(collection, { startedAt: { $gte: from, $lte: now } }, { sort: { startedAt: -1 }, limit: 10 });
97
- return {
98
- statusesRequests,
99
- prevStatusesRequests,
100
- monthlySuccess: DataframeManager_1.default.fill(rawMonthlySuccess !== null && rawMonthlySuccess !== void 0 ? rawMonthlySuccess : [], yearAgo, now),
101
- monthlyFails: DataframeManager_1.default.fill(rawMonthlyFails !== null && rawMonthlyFails !== void 0 ? rawMonthlyFails : [], yearAgo, now),
102
- consumers: consumers,
103
- topLine: DataframeManager_1.default.fill(topLines !== null && topLines !== void 0 ? topLines : [], yearAgo, now),
104
- topTime: DataframeManager_1.default.fill(topTime !== null && topTime !== void 0 ? topTime : [], yearAgo, now),
105
- recentExecution
106
- };
107
- });
108
- }
109
- getFilteredUsageDetails(from, to, filters) {
110
- return __awaiter(this, void 0, void 0, function* () {
111
- const now = DSTE_1.default.now();
112
- const fromDate = from || new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
113
- const toDate = to || now;
114
- const collection = 'usage';
115
- // Build match criteria
116
- const matchCriteria = {
117
- startedAt: { $gte: fromDate, $lte: toDate }
118
- };
119
- if (filters === null || filters === void 0 ? void 0 : filters.consumer) {
120
- matchCriteria.consumer = filters.consumer;
121
- }
122
- if (filters === null || filters === void 0 ? void 0 : filters.user) {
123
- matchCriteria['executedBy.name'] = filters.user;
124
- }
125
- if (filters === null || filters === void 0 ? void 0 : filters.status) {
126
- matchCriteria.status = filters.status;
127
- }
128
- // Query with filters and sorting
129
- const usageDetails = yield DatabaseEngine_1.default.query(collection, matchCriteria, {
130
- sort: { startedAt: -1 },
131
- limit: 1000 // Reasonable limit to prevent large result sets
132
- });
133
- return usageDetails;
134
- });
135
- }
136
- getUsageById(id) {
137
- return __awaiter(this, void 0, void 0, function* () {
138
- const collection = 'usage';
139
- try {
140
- const usageDetail = yield DatabaseEngine_1.default.get(collection, id);
141
- return usageDetail;
142
- }
143
- catch (error) {
144
- // If document not found or invalid ID, return null
145
- console.error('Error fetching usage by ID:', error);
146
- return null;
147
- }
148
- });
149
- }
150
- }
151
- exports.default = new UsageDataManager();
@@ -1,65 +0,0 @@
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 DSTE_1 = __importDefault(require("../../core/dste/DSTE"));
8
- const DatabaseEngine_1 = __importDefault(require("../../database/DatabaseEngine"));
9
- const Helper_1 = __importDefault(require("../../helper/Helper"));
10
- const Settings_1 = __importDefault(require("../../helper/Settings"));
11
- class UsageManagerClass {
12
- constructor() {
13
- /**
14
- * TODO: I need to group the usage stats into a bucket daily. When and how I do it is still a question...
15
- */
16
- this.getTodayBucketId = (consumer) => {
17
- (0, Affirm_1.default)(consumer, `Invalid consumer`);
18
- const now = DSTE_1.default.now();
19
- return `${consumer.name}_${now.getUTCFullYear()}_${now.getUTCMonth()}_${now.getUTCDate()}`.toLowerCase();
20
- };
21
- this.startUsage = (consumer, details) => {
22
- const { user, invokedBy } = details;
23
- const newUsage = {
24
- _id: Helper_1.default.uuid(),
25
- consumer: consumer.name,
26
- startedAt: DSTE_1.default.now(),
27
- executedBy: user,
28
- itemsCount: -1,
29
- status: 'started',
30
- invokedBy: invokedBy !== null && invokedBy !== void 0 ? invokedBy : 'UNKNOWN',
31
- _signature: '',
32
- stats: null
33
- };
34
- if (Helper_1.default.isDev())
35
- return { usageId: newUsage._id, usage: Promise.resolve(newUsage) };
36
- const updateRes = DatabaseEngine_1.default.upsert(Settings_1.default.db.collections.usage, newUsage._id, newUsage);
37
- return { usageId: newUsage._id, usage: updateRes };
38
- };
39
- this.endUsage = (usageId, itemsCount, stats) => {
40
- const update = {
41
- itemsCount: itemsCount,
42
- status: 'success',
43
- finishedAt: DSTE_1.default.now(),
44
- stats
45
- };
46
- if (Helper_1.default.isDev())
47
- return { usageId: null, usage: Promise.resolve(update) };
48
- const updateRes = DatabaseEngine_1.default.upsert(Settings_1.default.db.collections.usage, usageId, update);
49
- return { usageId: usageId, usage: updateRes };
50
- };
51
- this.failUsage = (usageId, error) => {
52
- const update = {
53
- status: 'failed',
54
- error: error,
55
- finishedAt: DSTE_1.default.now()
56
- };
57
- if (Helper_1.default.isDev())
58
- return { usageId: null, usage: Promise.resolve(update) };
59
- const updateRes = DatabaseEngine_1.default.upsert(Settings_1.default.db.collections.usage, usageId, update);
60
- return { usageId: usageId, usage: updateRes };
61
- };
62
- }
63
- }
64
- const UsageManager = new UsageManagerClass();
65
- exports.default = UsageManager;
@@ -1,216 +0,0 @@
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 Algo_1 = __importDefault(require("../../core/Algo"));
8
- const ConsumerManager_1 = __importDefault(require("../consumer/ConsumerManager"));
9
- const Environment_1 = __importDefault(require("../Environment"));
10
- class ValidatorClass {
11
- constructor() {
12
- this.validateSources = (sources) => {
13
- (0, Affirm_1.default)(sources, 'Invalid sources');
14
- const errors = [];
15
- try {
16
- const dupes = Algo_1.default.duplicatesObject(sources, 'name');
17
- if (dupes.length > 0)
18
- errors.push(`Duplicate name(s) found in sources: "${dupes.map(x => x.name).join(', ')}"`);
19
- for (let i = 0; i < sources.length; i++) {
20
- const source = sources[i];
21
- if (source.engine === 'local' && !source.authentication.path)
22
- errors.push(`For source ${source.name}, the path has not been configured`);
23
- }
24
- }
25
- catch (e) {
26
- if (errors.length === 0)
27
- errors.push(`There was an error in the validation Sources. (error: ${e})`);
28
- }
29
- return errors;
30
- };
31
- this.validateProducers = (producers) => {
32
- (0, Affirm_1.default)(producers, 'Invalid producers');
33
- const errors = [];
34
- try {
35
- const dupes = Algo_1.default.duplicatesObject(producers, 'name');
36
- if (dupes.length > 0)
37
- errors.push(`Duplicate name(s) found in producers: "${dupes.map(x => x.name).join(', ')}"`);
38
- }
39
- catch (e) {
40
- if (errors.length === 0)
41
- errors.push(`There was an error in the validation Producers. (error: ${e})`);
42
- }
43
- return errors;
44
- };
45
- this.validateProducer = (producer) => {
46
- (0, Affirm_1.default)(producer, 'Invalid producer');
47
- const errors = [];
48
- try {
49
- if (!producer.source || producer.source.length === 0)
50
- errors.push(`Missing parameter "source" in producer`);
51
- if (producer.dimensions.some(x => x.name.includes('{') || x.name.includes('[')))
52
- errors.push(`Invalid dimension name found in producer "${producer.name}": can't use characters "{" or "[" in dimension names`);
53
- // Validate sourceFilename dimension usage
54
- const sourceFilenameDimensions = producer.dimensions.filter(x => x.sourceFilename === true);
55
- if (sourceFilenameDimensions.length > 1) {
56
- errors.push(`Producer "${producer.name}" has multiple dimensions with sourceFilename=true. Only one dimension can have this flag.`);
57
- }
58
- if (sourceFilenameDimensions.length > 0) {
59
- const source = Environment_1.default.getSource(producer.source);
60
- if (source) {
61
- const validEngines = ['local', 'aws-s3'];
62
- if (!validEngines.includes(source.engine)) {
63
- errors.push(`Producer "${producer.name}" has a dimension with sourceFilename=true but the source engine "${source.engine}" doesn't support this feature. Only "local" and "aws-s3" sources support sourceFilename.`);
64
- }
65
- if (!producer.settings.fileKey && !producer.settings.fileType) {
66
- errors.push(`Producer "${producer.name}" has a dimension with sourceFilename=true but is not a file-based producer. sourceFilename requires fileKey and fileType to be set.`);
67
- }
68
- }
69
- }
70
- }
71
- catch (e) {
72
- if (errors.length === 0)
73
- errors.push(`There was an error in the validation Producer. (error: ${e})`);
74
- }
75
- return errors;
76
- };
77
- this.validateConsumers = (consumers) => {
78
- (0, Affirm_1.default)(consumers, 'Invalid consumers');
79
- const errors = [];
80
- try {
81
- const dupes = Algo_1.default.duplicatesObject(consumers, 'name');
82
- if (dupes.length > 0)
83
- errors.push(`Duplicate name(s) found in consumers: "${dupes.map(x => x.name).join(', ')}"`);
84
- }
85
- catch (e) {
86
- if (errors.length === 0)
87
- errors.push(`There was an error in the validation Consumers. (error: ${e})`);
88
- }
89
- return errors;
90
- };
91
- this.validateConsumer = (consumer) => {
92
- (0, Affirm_1.default)(consumer, 'Invalid consumer');
93
- const errors = [];
94
- try {
95
- // TODO: check that a consumer doesn't consume hitself
96
- const allFieldsWithNoFrom = consumer.fields.filter(x => x.key === '*' && !x.from);
97
- if (allFieldsWithNoFrom.length > 0 && consumer.producers.length > 1)
98
- errors.push(`Field with key "*" was used without specifying the "from" producer and multiple producers were found.`);
99
- // Validation on producers
100
- if (consumer.producers.length === 0)
101
- errors.push(`Consumer must have at least 1 producer.`);
102
- const producers = consumer.producers.map(x => Environment_1.default.getProducer(x.name));
103
- if (producers.length === 0)
104
- errors.push('No producers found');
105
- if (producers.some(x => !x))
106
- errors.push(`Invalid producer found in consumer "${consumer.name}"`);
107
- if (consumer.producers.length > 0) {
108
- const withJoins = consumer.producers.filter(x => (Algo_1.default.hasVal(x.joins) && x.joins.length > 0) || !x.union);
109
- const withUnions = consumer.producers.filter(x => x.union === true);
110
- if (withJoins.length > 0 && withUnions.length)
111
- errors.push(`Multiple producers in consumer have mixed "joins" and "union": you can either have multiple producers with "joins" or multiple producers with "union", but not both (joins: ${withJoins.map(x => x.name).join(', ')}; unions: ${withUnions.map(x => x.name).join(', ')})`);
112
- }
113
- // Validation on sources
114
- const sources = producers.map(x => Environment_1.default.getSource(x.source));
115
- if (sources.length === 0)
116
- errors.push('No sources found');
117
- if (sources.some(x => !x))
118
- errors.push(`Invalid source found in consumer "${consumer.name}"`);
119
- // For now we only support connecting producers of the same engine type to a consumer, so we give an error if we detect different ones
120
- const uniqEngines = Algo_1.default.uniqBy(sources, 'engine');
121
- if (uniqEngines.length !== 1)
122
- errors.push(`Sources with different engines are used in the consumer "${consumer.name}" (${uniqEngines.join(', ')})`);
123
- // For now we also only support consumers that have producers ALL having the same exact source
124
- const uniqNames = Algo_1.default.uniqBy(sources, 'name');
125
- if (uniqNames.length !== 1)
126
- errors.push(`Producers with different sources are used in the consumer "${consumer.name}" (${uniqNames.join(', ')})`);
127
- if (consumer.filters && consumer.filters.length > 0) {
128
- if (consumer.filters.some(x => x.sql && x.rule))
129
- errors.push(`A single consumer can't have both filters based on SQL and filters based on rules.`);
130
- const [source] = ConsumerManager_1.default.getSource(consumer);
131
- const engineClass = this.getEngineClass(source.engine);
132
- if (engineClass === 'file' && consumer.filters.some(x => x.sql))
133
- errors.push(`Filters based on SQL are only valid for SQL based sources. (source: ${source.name})`);
134
- if (engineClass === 'sql' && consumer.filters.some(x => x.rule))
135
- errors.push(`Filters based on rules are only valid for non-SQL based sources. (source: ${source.name})`);
136
- // Check that the members of the rules are present in the consumer
137
- const allFields = consumer.fields;
138
- const ruleBasedFilters = consumer.filters.filter(x => x.rule);
139
- const rulesWithMatchingFields = ruleBasedFilters.map(x => ({
140
- match: allFields.find(k => { var _a; return ((_a = k.alias) !== null && _a !== void 0 ? _a : k.key) === x.rule.member; }),
141
- rule: x
142
- }));
143
- if (rulesWithMatchingFields.some(x => !x.match)) {
144
- const missingRules = rulesWithMatchingFields.filter(x => !x.match);
145
- errors.push(`Filter(s) on member(s) "${missingRules.map(x => x.rule.rule.member).join(', ')}" is invalid since the member specified is not present in the consumer. Check the member value or add the missing field to the consumer.`);
146
- }
147
- }
148
- const validateTransformations = (fields) => {
149
- var _a;
150
- const errors = [];
151
- const trxsFields = fields.filter(x => x.transform);
152
- for (const field of trxsFields) {
153
- const trxToValidate = [];
154
- if (Array.isArray(field.transform))
155
- trxToValidate.push(...field.transform);
156
- else
157
- trxToValidate.push(field.transform);
158
- for (const trans of trxToValidate) {
159
- if ('combine_fields' in trans) {
160
- const { combine_fields } = trans;
161
- if (!combine_fields.fields || combine_fields.fields.length === 0)
162
- errors.push(`The "combine_field" transformation is missing the "fields" property ("${field.key}").`);
163
- const missingFieldsInConsumer = combine_fields.fields
164
- .map(x => ({ field: x, found: fields.find(k => { var _a; return ((_a = k.alias) !== null && _a !== void 0 ? _a : k.key) === x; }) }))
165
- .filter(x => !x.found);
166
- if (missingFieldsInConsumer.length > 0)
167
- errors.push(`The requested field(s) for a transformation is missing in the consumer -> missing field(s): "${missingFieldsInConsumer.map(x => x.field).join(', ')}"; field transformation: "${(_a = field.alias) !== null && _a !== void 0 ? _a : field.key}";`);
168
- }
169
- }
170
- }
171
- return errors;
172
- };
173
- errors.push(...validateTransformations(consumer.fields));
174
- // Validation outputs
175
- const duplicatesOutputs = Algo_1.default.duplicatesObject(consumer.outputs, 'format');
176
- if (duplicatesOutputs.length > 0) {
177
- const duplicatesTypes = Algo_1.default.uniq(duplicatesOutputs.map(x => x.format));
178
- errors.push(`There are outputs with the same type. (duplicates type: ${duplicatesTypes.join(' and ')})`);
179
- }
180
- for (const output of consumer.outputs) {
181
- const format = output.format.toUpperCase();
182
- if (format === 'SQL' && output.accelerated && output.direct)
183
- errors.push(`An output SQL cannot be both direct and accelerated (output: ${format})`);
184
- if ((format === 'CSV' || format === 'JSON' || format === 'PARQUET')) {
185
- if (!output.exportDestination)
186
- errors.push(`A static file output must have an export destination set (${format})`);
187
- else if (!Environment_1.default.getSource(output.exportDestination))
188
- errors.push(`The export destination "${output.exportDestination}" was not found in the sources.`);
189
- }
190
- }
191
- // Validate distinct
192
- if (consumer.options) {
193
- if (Algo_1.default.hasVal(consumer.options.distinct) && Algo_1.default.hasVal(consumer.options.distinctOn))
194
- errors.push(`Can't specify a "distinct" and a "distinctOn" clause on the same consumer (${consumer.name}); use one or the other.`);
195
- }
196
- }
197
- catch (e) {
198
- if (errors.length === 0)
199
- errors.push(`There was an error in the validation Consumer. (error: ${e})`);
200
- }
201
- return errors;
202
- };
203
- this.getEngineClass = (engine) => {
204
- switch (engine) {
205
- case 'aws-dynamodb': return 'no-sql';
206
- case 'aws-redshift':
207
- case 'postgres': return 'sql';
208
- case 'delta-share':
209
- case 'aws-s3': return 'file';
210
- case 'local': return 'local';
211
- }
212
- };
213
- }
214
- }
215
- const Validator = new ValidatorClass();
216
- exports.default = Validator;