@forzalabs/remora 1.1.12 → 1.1.14
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/index.js +153 -58
- package/package.json +1 -1
- package/workers/ExecutorWorker.js +153 -58
package/index.js
CHANGED
|
@@ -13500,7 +13500,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
|
|
|
13500
13500
|
|
|
13501
13501
|
// ../../packages/constants/src/Constants.ts
|
|
13502
13502
|
var CONSTANTS = {
|
|
13503
|
-
cliVersion: "1.1.
|
|
13503
|
+
cliVersion: "1.1.14",
|
|
13504
13504
|
backendVersion: 1,
|
|
13505
13505
|
backendPort: 5088,
|
|
13506
13506
|
workerVersion: 2,
|
|
@@ -13672,6 +13672,42 @@ var SchemaValidatorClass = class {
|
|
|
13672
13672
|
var SchemaValidator = new SchemaValidatorClass();
|
|
13673
13673
|
var SchemaValidator_default = SchemaValidator;
|
|
13674
13674
|
|
|
13675
|
+
// ../../packages/common/src/ResourcesUtils.ts
|
|
13676
|
+
var ResourcesUtilsClass = class {
|
|
13677
|
+
constructor() {
|
|
13678
|
+
this.getAvailableColumns = (consumer) => {
|
|
13679
|
+
return consumer.producers.flatMap((cProd) => {
|
|
13680
|
+
const producer = Environment_default.getProducer(cProd.name);
|
|
13681
|
+
if (!producer) {
|
|
13682
|
+
const subConsumer = Environment_default.getConsumer(cProd.name);
|
|
13683
|
+
Affirm_default(subConsumer, `No producer found with name "${cProd.name}"`);
|
|
13684
|
+
return this.getAvailableColumns(subConsumer);
|
|
13685
|
+
} else {
|
|
13686
|
+
const dims = producer.dimensions.map((x) => ({
|
|
13687
|
+
consumerAlias: null,
|
|
13688
|
+
consumerKey: null,
|
|
13689
|
+
nameInProducer: x.name,
|
|
13690
|
+
aliasInProducer: x.alias,
|
|
13691
|
+
dimension: x,
|
|
13692
|
+
owner: cProd.name
|
|
13693
|
+
}));
|
|
13694
|
+
const meas = producer.measures?.map((x) => ({
|
|
13695
|
+
consumerAlias: null,
|
|
13696
|
+
consumerKey: null,
|
|
13697
|
+
nameInProducer: x.name,
|
|
13698
|
+
aliasInProducer: x.name,
|
|
13699
|
+
measure: x,
|
|
13700
|
+
owner: cProd.name
|
|
13701
|
+
})) ?? [];
|
|
13702
|
+
return [...dims, ...meas];
|
|
13703
|
+
}
|
|
13704
|
+
});
|
|
13705
|
+
};
|
|
13706
|
+
}
|
|
13707
|
+
};
|
|
13708
|
+
var ResourcesUtils = new ResourcesUtilsClass();
|
|
13709
|
+
var ResourcesUtils_default = ResourcesUtils;
|
|
13710
|
+
|
|
13675
13711
|
// ../../packages/common/src/validation/Validator.ts
|
|
13676
13712
|
var ValidatorClass = class {
|
|
13677
13713
|
constructor() {
|
|
@@ -13782,6 +13818,38 @@ var ValidatorClass = class {
|
|
|
13782
13818
|
const uniqNames = Algo_default.uniqBy(sources, "name");
|
|
13783
13819
|
if (uniqNames.length !== 1)
|
|
13784
13820
|
errors.push(`Producers with different sources are used in the consumer "${consumer.name}" (${uniqNames.join(", ")})`);
|
|
13821
|
+
const availableColumns = ResourcesUtils_default.getAvailableColumns(consumer);
|
|
13822
|
+
const availableFieldsByProducer = /* @__PURE__ */ new Map();
|
|
13823
|
+
for (const col of availableColumns) {
|
|
13824
|
+
const existing = availableFieldsByProducer.get(col.owner) ?? [];
|
|
13825
|
+
if (!existing.includes(col.nameInProducer))
|
|
13826
|
+
existing.push(col.nameInProducer);
|
|
13827
|
+
availableFieldsByProducer.set(col.owner, existing);
|
|
13828
|
+
}
|
|
13829
|
+
const allAvailableFields = [...availableFieldsByProducer.values()].flat();
|
|
13830
|
+
for (const field of consumer.fields) {
|
|
13831
|
+
if (field.key === "*" || field.fixed || field.copyFrom) continue;
|
|
13832
|
+
if (field.from) {
|
|
13833
|
+
const producerFields = availableFieldsByProducer.get(field.from);
|
|
13834
|
+
if (producerFields && !producerFields.includes(field.key))
|
|
13835
|
+
errors.push(`Field "${field.key}" (from: "${field.from}") is not found in producer "${field.from}" in consumer "${consumer.name}". Available fields: ${producerFields.join(", ")}`);
|
|
13836
|
+
} else if (consumer.producers.length === 1) {
|
|
13837
|
+
const producerFields = availableFieldsByProducer.get(consumer.producers[0].name);
|
|
13838
|
+
if (producerFields && !producerFields.includes(field.key))
|
|
13839
|
+
errors.push(`Field "${field.key}" is not found in producer "${consumer.producers[0].name}" in consumer "${consumer.name}". Available fields: ${producerFields.join(", ")}`);
|
|
13840
|
+
} else {
|
|
13841
|
+
if (allAvailableFields.length > 0 && !allAvailableFields.includes(field.key))
|
|
13842
|
+
errors.push(`Field "${field.key}" is not found in any of the producers of consumer "${consumer.name}".`);
|
|
13843
|
+
}
|
|
13844
|
+
}
|
|
13845
|
+
for (let i = 0; i < consumer.fields.length; i++) {
|
|
13846
|
+
const field = consumer.fields[i];
|
|
13847
|
+
if (!field.copyFrom) continue;
|
|
13848
|
+
const precedingFields = consumer.fields.slice(0, i);
|
|
13849
|
+
const found = precedingFields.find((f) => (f.alias ?? f.key) === field.copyFrom);
|
|
13850
|
+
if (!found)
|
|
13851
|
+
errors.push(`Field "${field.alias ?? field.key}" uses copyFrom "${field.copyFrom}" but no field with that name/alias exists before it in consumer "${consumer.name}".`);
|
|
13852
|
+
}
|
|
13785
13853
|
if (consumer.filters && consumer.filters.length > 0) {
|
|
13786
13854
|
if (consumer.filters.some((x) => x.sql && x.rule))
|
|
13787
13855
|
errors.push(`A single consumer can't have both filters based on SQL and filters based on rules.`);
|
|
@@ -13854,6 +13922,20 @@ var ValidatorClass = class {
|
|
|
13854
13922
|
if (consumer.options) {
|
|
13855
13923
|
if (Algo_default.hasVal(consumer.options.distinct) && Algo_default.hasVal(consumer.options.distinctOn))
|
|
13856
13924
|
errors.push(`Can't specify a "distinct" and a "distinctOn" clause on the same consumer (${consumer.name}); use one or the other.`);
|
|
13925
|
+
if (Algo_default.hasVal(consumer.options.distinctOn)) {
|
|
13926
|
+
const { distinctOn } = consumer.options;
|
|
13927
|
+
const hasWildcard = consumer.fields.some((x) => x.key === "*");
|
|
13928
|
+
const consumerFieldKeys = consumer.fields.map((x) => x.alias ?? x.key);
|
|
13929
|
+
if (!hasWildcard) {
|
|
13930
|
+
const missingKeys = distinctOn.keys.filter((k) => !consumerFieldKeys.includes(k));
|
|
13931
|
+
if (missingKeys.length > 0)
|
|
13932
|
+
errors.push(`distinctOn references key(s) "${missingKeys.join(", ")}" that are not present in the consumer "${consumer.name}".`);
|
|
13933
|
+
}
|
|
13934
|
+
if (distinctOn.resolution?.orderBy && !hasWildcard) {
|
|
13935
|
+
if (!consumerFieldKeys.includes(distinctOn.resolution.orderBy))
|
|
13936
|
+
errors.push(`distinctOn resolution orderBy field "${distinctOn.resolution.orderBy}" is not present in the consumer "${consumer.name}".`);
|
|
13937
|
+
}
|
|
13938
|
+
}
|
|
13857
13939
|
if (Algo_default.hasVal(consumer.options.pivot)) {
|
|
13858
13940
|
if (Algo_default.hasVal(consumer.options.distinct) || Algo_default.hasVal(consumer.options.distinctOn))
|
|
13859
13941
|
errors.push(`Can't specify "pivot" together with "distinct" or "distinctOn" on the same consumer (${consumer.name}).`);
|
|
@@ -16155,8 +16237,15 @@ var DatabaseEngineClass = class {
|
|
|
16155
16237
|
this.MAX_TRY_CONNECTION = 3;
|
|
16156
16238
|
this.db = () => this._db;
|
|
16157
16239
|
this.connect = async () => {
|
|
16158
|
-
|
|
16159
|
-
|
|
16240
|
+
if (!ProcessENVManager_default.getEnvVariable("MONGO_URI")) {
|
|
16241
|
+
if (Helper_default.isDev()) {
|
|
16242
|
+
this._uri = "mongodb://mongo:27017/remora";
|
|
16243
|
+
} else {
|
|
16244
|
+
this._uri = "mongodb://localhost:27017/remora";
|
|
16245
|
+
}
|
|
16246
|
+
} else {
|
|
16247
|
+
this._uri = ProcessENVManager_default.getEnvVariable("MONGO_URI");
|
|
16248
|
+
}
|
|
16160
16249
|
const errors = [];
|
|
16161
16250
|
this._client = new import_mongodb.MongoClient(this._uri);
|
|
16162
16251
|
for (let i = 0; i < this.MAX_TRY_CONNECTION; i++) {
|
|
@@ -17734,33 +17823,7 @@ var ConsumerManagerClass = class {
|
|
|
17734
17823
|
return expandedFields;
|
|
17735
17824
|
};
|
|
17736
17825
|
this.getAvailableColumns = (consumer) => {
|
|
17737
|
-
|
|
17738
|
-
const producer = Environment_default.getProducer(cProd.name);
|
|
17739
|
-
if (!producer) {
|
|
17740
|
-
const subConsumer = Environment_default.getConsumer(cProd.name);
|
|
17741
|
-
Affirm_default(subConsumer, `No producer found with name "${cProd.name}"`);
|
|
17742
|
-
return this.getAvailableColumns(subConsumer);
|
|
17743
|
-
} else {
|
|
17744
|
-
const dims = producer.dimensions.map((x) => ({
|
|
17745
|
-
consumerAlias: null,
|
|
17746
|
-
consumerKey: null,
|
|
17747
|
-
nameInProducer: x.name,
|
|
17748
|
-
aliasInProducer: x.alias,
|
|
17749
|
-
dimension: x,
|
|
17750
|
-
owner: cProd.name
|
|
17751
|
-
}));
|
|
17752
|
-
const meas = producer.measures?.map((x) => ({
|
|
17753
|
-
consumerAlias: null,
|
|
17754
|
-
consumerKey: null,
|
|
17755
|
-
nameInProducer: x.name,
|
|
17756
|
-
aliasInProducer: x.name,
|
|
17757
|
-
measure: x,
|
|
17758
|
-
owner: cProd.name
|
|
17759
|
-
})) ?? [];
|
|
17760
|
-
return [...dims, ...meas];
|
|
17761
|
-
}
|
|
17762
|
-
});
|
|
17763
|
-
return availableColumns;
|
|
17826
|
+
return ResourcesUtils_default.getAvailableColumns(consumer);
|
|
17764
17827
|
};
|
|
17765
17828
|
this.expandField = (consumer, field, availableColumns) => {
|
|
17766
17829
|
Affirm_default(consumer, "Invalid consumer");
|
|
@@ -18848,18 +18911,24 @@ var ConsumerExecutorClass = class {
|
|
|
18848
18911
|
for (const field of fields) {
|
|
18849
18912
|
const { cField } = field;
|
|
18850
18913
|
const fieldKey = cField.alias ?? cField.key;
|
|
18851
|
-
|
|
18852
|
-
|
|
18853
|
-
if (
|
|
18854
|
-
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
18860
|
-
|
|
18861
|
-
|
|
18862
|
-
|
|
18914
|
+
try {
|
|
18915
|
+
const dimension = dimensions.find((x) => x.name === cField.key);
|
|
18916
|
+
if (!dimension) {
|
|
18917
|
+
if (cField.fixed && Algo_default.hasVal(cField.default))
|
|
18918
|
+
record[fieldKey] = cField.default;
|
|
18919
|
+
else if (cField.copyFrom)
|
|
18920
|
+
record[fieldKey] = record[cField.copyFrom];
|
|
18921
|
+
else
|
|
18922
|
+
throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
|
|
18923
|
+
}
|
|
18924
|
+
if (cField.alias && cField.alias !== dimension.name) {
|
|
18925
|
+
record[cField.alias] = record[dimension.name];
|
|
18926
|
+
delete record[dimension.name];
|
|
18927
|
+
}
|
|
18928
|
+
} catch (error) {
|
|
18929
|
+
const err = new Error(`Field mapping failed for field "${fieldKey}" of producer "${producer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18930
|
+
Logger_default.error(err);
|
|
18931
|
+
throw err;
|
|
18863
18932
|
}
|
|
18864
18933
|
}
|
|
18865
18934
|
for (const field of fields) {
|
|
@@ -18878,28 +18947,51 @@ var ConsumerExecutorClass = class {
|
|
|
18878
18947
|
case "skip":
|
|
18879
18948
|
continue;
|
|
18880
18949
|
case "fail":
|
|
18881
|
-
default:
|
|
18882
|
-
|
|
18950
|
+
default: {
|
|
18951
|
+
const err = new Error(errorMessage, { cause: error });
|
|
18952
|
+
Logger_default.error(err);
|
|
18953
|
+
throw err;
|
|
18954
|
+
}
|
|
18883
18955
|
}
|
|
18884
18956
|
} else {
|
|
18885
|
-
|
|
18957
|
+
const err = new Error(errorMessage, { cause: error });
|
|
18958
|
+
Logger_default.error(err);
|
|
18959
|
+
throw err;
|
|
18886
18960
|
}
|
|
18887
18961
|
}
|
|
18888
18962
|
}
|
|
18889
|
-
|
|
18890
|
-
const
|
|
18891
|
-
|
|
18892
|
-
|
|
18963
|
+
try {
|
|
18964
|
+
for (const dimension of dimensions) {
|
|
18965
|
+
const field = fields.find((x) => x.cField.key === dimension.name);
|
|
18966
|
+
if (!field)
|
|
18967
|
+
delete record[dimension.name];
|
|
18968
|
+
}
|
|
18969
|
+
} catch (error) {
|
|
18970
|
+
const err = new Error(`Removing unmapped dimensions failed for producer "${producer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18971
|
+
Logger_default.error(err);
|
|
18972
|
+
throw err;
|
|
18893
18973
|
}
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18974
|
+
try {
|
|
18975
|
+
if (consumer.filters && consumer.filters.length > 0) {
|
|
18976
|
+
const isKept = consumer.filters.every((x) => RequestExecutor_default.evaluateFilter(record, x.rule));
|
|
18977
|
+
if (!isKept)
|
|
18978
|
+
return null;
|
|
18979
|
+
}
|
|
18980
|
+
} catch (error) {
|
|
18981
|
+
const err = new Error(`Consumer filter evaluation failed for consumer "${consumer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18982
|
+
Logger_default.error(err);
|
|
18983
|
+
throw err;
|
|
18898
18984
|
}
|
|
18899
|
-
|
|
18900
|
-
|
|
18901
|
-
|
|
18902
|
-
|
|
18985
|
+
try {
|
|
18986
|
+
if (requestOptions && requestOptions.filters) {
|
|
18987
|
+
const isKept = requestOptions.filters.every((x) => RequestExecutor_default.evaluateFilter(record, x));
|
|
18988
|
+
if (!isKept)
|
|
18989
|
+
return null;
|
|
18990
|
+
}
|
|
18991
|
+
} catch (error) {
|
|
18992
|
+
const err = new Error(`Request filter evaluation failed for consumer "${consumer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18993
|
+
Logger_default.error(err);
|
|
18994
|
+
throw err;
|
|
18903
18995
|
}
|
|
18904
18996
|
return record;
|
|
18905
18997
|
};
|
|
@@ -19416,6 +19508,9 @@ var ExecutorOrchestratorClass = class {
|
|
|
19416
19508
|
Logger_default.log(`[${usageId}] Spawning worker ${workerId} for producer "${prod.name}" \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
|
|
19417
19509
|
workerThreads.push(pool.exec("executor", [workerData], {
|
|
19418
19510
|
on: (payload) => this.onWorkAdvanced(payload, currentWorkerIndex, _progress)
|
|
19511
|
+
}).catch((error) => {
|
|
19512
|
+
Logger_default.error(error);
|
|
19513
|
+
return null;
|
|
19419
19514
|
}));
|
|
19420
19515
|
}
|
|
19421
19516
|
Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
|
|
@@ -19481,7 +19576,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
19481
19576
|
return finalResult;
|
|
19482
19577
|
} catch (error) {
|
|
19483
19578
|
Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
|
|
19484
|
-
Logger_default.error(
|
|
19579
|
+
Logger_default.error(error);
|
|
19485
19580
|
await pool.terminate();
|
|
19486
19581
|
await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
|
|
19487
19582
|
Logger_default.log(`[${usageId}] Running cleanup after failure`);
|
package/package.json
CHANGED
|
@@ -13494,7 +13494,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
|
|
|
13494
13494
|
|
|
13495
13495
|
// ../../packages/constants/src/Constants.ts
|
|
13496
13496
|
var CONSTANTS = {
|
|
13497
|
-
cliVersion: "1.1.
|
|
13497
|
+
cliVersion: "1.1.14",
|
|
13498
13498
|
backendVersion: 1,
|
|
13499
13499
|
backendPort: 5088,
|
|
13500
13500
|
workerVersion: 2,
|
|
@@ -13666,6 +13666,42 @@ var SchemaValidatorClass = class {
|
|
|
13666
13666
|
var SchemaValidator = new SchemaValidatorClass();
|
|
13667
13667
|
var SchemaValidator_default = SchemaValidator;
|
|
13668
13668
|
|
|
13669
|
+
// ../../packages/common/src/ResourcesUtils.ts
|
|
13670
|
+
var ResourcesUtilsClass = class {
|
|
13671
|
+
constructor() {
|
|
13672
|
+
this.getAvailableColumns = (consumer) => {
|
|
13673
|
+
return consumer.producers.flatMap((cProd) => {
|
|
13674
|
+
const producer = Environment_default.getProducer(cProd.name);
|
|
13675
|
+
if (!producer) {
|
|
13676
|
+
const subConsumer = Environment_default.getConsumer(cProd.name);
|
|
13677
|
+
Affirm_default(subConsumer, `No producer found with name "${cProd.name}"`);
|
|
13678
|
+
return this.getAvailableColumns(subConsumer);
|
|
13679
|
+
} else {
|
|
13680
|
+
const dims = producer.dimensions.map((x) => ({
|
|
13681
|
+
consumerAlias: null,
|
|
13682
|
+
consumerKey: null,
|
|
13683
|
+
nameInProducer: x.name,
|
|
13684
|
+
aliasInProducer: x.alias,
|
|
13685
|
+
dimension: x,
|
|
13686
|
+
owner: cProd.name
|
|
13687
|
+
}));
|
|
13688
|
+
const meas = producer.measures?.map((x) => ({
|
|
13689
|
+
consumerAlias: null,
|
|
13690
|
+
consumerKey: null,
|
|
13691
|
+
nameInProducer: x.name,
|
|
13692
|
+
aliasInProducer: x.name,
|
|
13693
|
+
measure: x,
|
|
13694
|
+
owner: cProd.name
|
|
13695
|
+
})) ?? [];
|
|
13696
|
+
return [...dims, ...meas];
|
|
13697
|
+
}
|
|
13698
|
+
});
|
|
13699
|
+
};
|
|
13700
|
+
}
|
|
13701
|
+
};
|
|
13702
|
+
var ResourcesUtils = new ResourcesUtilsClass();
|
|
13703
|
+
var ResourcesUtils_default = ResourcesUtils;
|
|
13704
|
+
|
|
13669
13705
|
// ../../packages/common/src/validation/Validator.ts
|
|
13670
13706
|
var ValidatorClass = class {
|
|
13671
13707
|
constructor() {
|
|
@@ -13776,6 +13812,38 @@ var ValidatorClass = class {
|
|
|
13776
13812
|
const uniqNames = Algo_default.uniqBy(sources, "name");
|
|
13777
13813
|
if (uniqNames.length !== 1)
|
|
13778
13814
|
errors.push(`Producers with different sources are used in the consumer "${consumer.name}" (${uniqNames.join(", ")})`);
|
|
13815
|
+
const availableColumns = ResourcesUtils_default.getAvailableColumns(consumer);
|
|
13816
|
+
const availableFieldsByProducer = /* @__PURE__ */ new Map();
|
|
13817
|
+
for (const col of availableColumns) {
|
|
13818
|
+
const existing = availableFieldsByProducer.get(col.owner) ?? [];
|
|
13819
|
+
if (!existing.includes(col.nameInProducer))
|
|
13820
|
+
existing.push(col.nameInProducer);
|
|
13821
|
+
availableFieldsByProducer.set(col.owner, existing);
|
|
13822
|
+
}
|
|
13823
|
+
const allAvailableFields = [...availableFieldsByProducer.values()].flat();
|
|
13824
|
+
for (const field of consumer.fields) {
|
|
13825
|
+
if (field.key === "*" || field.fixed || field.copyFrom) continue;
|
|
13826
|
+
if (field.from) {
|
|
13827
|
+
const producerFields = availableFieldsByProducer.get(field.from);
|
|
13828
|
+
if (producerFields && !producerFields.includes(field.key))
|
|
13829
|
+
errors.push(`Field "${field.key}" (from: "${field.from}") is not found in producer "${field.from}" in consumer "${consumer.name}". Available fields: ${producerFields.join(", ")}`);
|
|
13830
|
+
} else if (consumer.producers.length === 1) {
|
|
13831
|
+
const producerFields = availableFieldsByProducer.get(consumer.producers[0].name);
|
|
13832
|
+
if (producerFields && !producerFields.includes(field.key))
|
|
13833
|
+
errors.push(`Field "${field.key}" is not found in producer "${consumer.producers[0].name}" in consumer "${consumer.name}". Available fields: ${producerFields.join(", ")}`);
|
|
13834
|
+
} else {
|
|
13835
|
+
if (allAvailableFields.length > 0 && !allAvailableFields.includes(field.key))
|
|
13836
|
+
errors.push(`Field "${field.key}" is not found in any of the producers of consumer "${consumer.name}".`);
|
|
13837
|
+
}
|
|
13838
|
+
}
|
|
13839
|
+
for (let i = 0; i < consumer.fields.length; i++) {
|
|
13840
|
+
const field = consumer.fields[i];
|
|
13841
|
+
if (!field.copyFrom) continue;
|
|
13842
|
+
const precedingFields = consumer.fields.slice(0, i);
|
|
13843
|
+
const found = precedingFields.find((f) => (f.alias ?? f.key) === field.copyFrom);
|
|
13844
|
+
if (!found)
|
|
13845
|
+
errors.push(`Field "${field.alias ?? field.key}" uses copyFrom "${field.copyFrom}" but no field with that name/alias exists before it in consumer "${consumer.name}".`);
|
|
13846
|
+
}
|
|
13779
13847
|
if (consumer.filters && consumer.filters.length > 0) {
|
|
13780
13848
|
if (consumer.filters.some((x) => x.sql && x.rule))
|
|
13781
13849
|
errors.push(`A single consumer can't have both filters based on SQL and filters based on rules.`);
|
|
@@ -13848,6 +13916,20 @@ var ValidatorClass = class {
|
|
|
13848
13916
|
if (consumer.options) {
|
|
13849
13917
|
if (Algo_default.hasVal(consumer.options.distinct) && Algo_default.hasVal(consumer.options.distinctOn))
|
|
13850
13918
|
errors.push(`Can't specify a "distinct" and a "distinctOn" clause on the same consumer (${consumer.name}); use one or the other.`);
|
|
13919
|
+
if (Algo_default.hasVal(consumer.options.distinctOn)) {
|
|
13920
|
+
const { distinctOn } = consumer.options;
|
|
13921
|
+
const hasWildcard = consumer.fields.some((x) => x.key === "*");
|
|
13922
|
+
const consumerFieldKeys = consumer.fields.map((x) => x.alias ?? x.key);
|
|
13923
|
+
if (!hasWildcard) {
|
|
13924
|
+
const missingKeys = distinctOn.keys.filter((k) => !consumerFieldKeys.includes(k));
|
|
13925
|
+
if (missingKeys.length > 0)
|
|
13926
|
+
errors.push(`distinctOn references key(s) "${missingKeys.join(", ")}" that are not present in the consumer "${consumer.name}".`);
|
|
13927
|
+
}
|
|
13928
|
+
if (distinctOn.resolution?.orderBy && !hasWildcard) {
|
|
13929
|
+
if (!consumerFieldKeys.includes(distinctOn.resolution.orderBy))
|
|
13930
|
+
errors.push(`distinctOn resolution orderBy field "${distinctOn.resolution.orderBy}" is not present in the consumer "${consumer.name}".`);
|
|
13931
|
+
}
|
|
13932
|
+
}
|
|
13851
13933
|
if (Algo_default.hasVal(consumer.options.pivot)) {
|
|
13852
13934
|
if (Algo_default.hasVal(consumer.options.distinct) || Algo_default.hasVal(consumer.options.distinctOn))
|
|
13853
13935
|
errors.push(`Can't specify "pivot" together with "distinct" or "distinctOn" on the same consumer (${consumer.name}).`);
|
|
@@ -17071,33 +17153,7 @@ var ConsumerManagerClass = class {
|
|
|
17071
17153
|
return expandedFields;
|
|
17072
17154
|
};
|
|
17073
17155
|
this.getAvailableColumns = (consumer) => {
|
|
17074
|
-
|
|
17075
|
-
const producer = Environment_default.getProducer(cProd.name);
|
|
17076
|
-
if (!producer) {
|
|
17077
|
-
const subConsumer = Environment_default.getConsumer(cProd.name);
|
|
17078
|
-
Affirm_default(subConsumer, `No producer found with name "${cProd.name}"`);
|
|
17079
|
-
return this.getAvailableColumns(subConsumer);
|
|
17080
|
-
} else {
|
|
17081
|
-
const dims = producer.dimensions.map((x) => ({
|
|
17082
|
-
consumerAlias: null,
|
|
17083
|
-
consumerKey: null,
|
|
17084
|
-
nameInProducer: x.name,
|
|
17085
|
-
aliasInProducer: x.alias,
|
|
17086
|
-
dimension: x,
|
|
17087
|
-
owner: cProd.name
|
|
17088
|
-
}));
|
|
17089
|
-
const meas = producer.measures?.map((x) => ({
|
|
17090
|
-
consumerAlias: null,
|
|
17091
|
-
consumerKey: null,
|
|
17092
|
-
nameInProducer: x.name,
|
|
17093
|
-
aliasInProducer: x.name,
|
|
17094
|
-
measure: x,
|
|
17095
|
-
owner: cProd.name
|
|
17096
|
-
})) ?? [];
|
|
17097
|
-
return [...dims, ...meas];
|
|
17098
|
-
}
|
|
17099
|
-
});
|
|
17100
|
-
return availableColumns;
|
|
17156
|
+
return ResourcesUtils_default.getAvailableColumns(consumer);
|
|
17101
17157
|
};
|
|
17102
17158
|
this.expandField = (consumer, field, availableColumns) => {
|
|
17103
17159
|
Affirm_default(consumer, "Invalid consumer");
|
|
@@ -17809,8 +17865,15 @@ var DatabaseEngineClass = class {
|
|
|
17809
17865
|
this.MAX_TRY_CONNECTION = 3;
|
|
17810
17866
|
this.db = () => this._db;
|
|
17811
17867
|
this.connect = async () => {
|
|
17812
|
-
|
|
17813
|
-
|
|
17868
|
+
if (!ProcessENVManager_default.getEnvVariable("MONGO_URI")) {
|
|
17869
|
+
if (Helper_default.isDev()) {
|
|
17870
|
+
this._uri = "mongodb://mongo:27017/remora";
|
|
17871
|
+
} else {
|
|
17872
|
+
this._uri = "mongodb://localhost:27017/remora";
|
|
17873
|
+
}
|
|
17874
|
+
} else {
|
|
17875
|
+
this._uri = ProcessENVManager_default.getEnvVariable("MONGO_URI");
|
|
17876
|
+
}
|
|
17814
17877
|
const errors = [];
|
|
17815
17878
|
this._client = new import_mongodb.MongoClient(this._uri);
|
|
17816
17879
|
for (let i = 0; i < this.MAX_TRY_CONNECTION; i++) {
|
|
@@ -18447,18 +18510,24 @@ var ConsumerExecutorClass = class {
|
|
|
18447
18510
|
for (const field of fields) {
|
|
18448
18511
|
const { cField } = field;
|
|
18449
18512
|
const fieldKey = cField.alias ?? cField.key;
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
if (
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18513
|
+
try {
|
|
18514
|
+
const dimension = dimensions.find((x) => x.name === cField.key);
|
|
18515
|
+
if (!dimension) {
|
|
18516
|
+
if (cField.fixed && Algo_default.hasVal(cField.default))
|
|
18517
|
+
record[fieldKey] = cField.default;
|
|
18518
|
+
else if (cField.copyFrom)
|
|
18519
|
+
record[fieldKey] = record[cField.copyFrom];
|
|
18520
|
+
else
|
|
18521
|
+
throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
|
|
18522
|
+
}
|
|
18523
|
+
if (cField.alias && cField.alias !== dimension.name) {
|
|
18524
|
+
record[cField.alias] = record[dimension.name];
|
|
18525
|
+
delete record[dimension.name];
|
|
18526
|
+
}
|
|
18527
|
+
} catch (error) {
|
|
18528
|
+
const err = new Error(`Field mapping failed for field "${fieldKey}" of producer "${producer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18529
|
+
Logger_default.error(err);
|
|
18530
|
+
throw err;
|
|
18462
18531
|
}
|
|
18463
18532
|
}
|
|
18464
18533
|
for (const field of fields) {
|
|
@@ -18477,28 +18546,51 @@ var ConsumerExecutorClass = class {
|
|
|
18477
18546
|
case "skip":
|
|
18478
18547
|
continue;
|
|
18479
18548
|
case "fail":
|
|
18480
|
-
default:
|
|
18481
|
-
|
|
18549
|
+
default: {
|
|
18550
|
+
const err = new Error(errorMessage, { cause: error });
|
|
18551
|
+
Logger_default.error(err);
|
|
18552
|
+
throw err;
|
|
18553
|
+
}
|
|
18482
18554
|
}
|
|
18483
18555
|
} else {
|
|
18484
|
-
|
|
18556
|
+
const err = new Error(errorMessage, { cause: error });
|
|
18557
|
+
Logger_default.error(err);
|
|
18558
|
+
throw err;
|
|
18485
18559
|
}
|
|
18486
18560
|
}
|
|
18487
18561
|
}
|
|
18488
|
-
|
|
18489
|
-
const
|
|
18490
|
-
|
|
18491
|
-
|
|
18562
|
+
try {
|
|
18563
|
+
for (const dimension of dimensions) {
|
|
18564
|
+
const field = fields.find((x) => x.cField.key === dimension.name);
|
|
18565
|
+
if (!field)
|
|
18566
|
+
delete record[dimension.name];
|
|
18567
|
+
}
|
|
18568
|
+
} catch (error) {
|
|
18569
|
+
const err = new Error(`Removing unmapped dimensions failed for producer "${producer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18570
|
+
Logger_default.error(err);
|
|
18571
|
+
throw err;
|
|
18492
18572
|
}
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18573
|
+
try {
|
|
18574
|
+
if (consumer.filters && consumer.filters.length > 0) {
|
|
18575
|
+
const isKept = consumer.filters.every((x) => RequestExecutor_default.evaluateFilter(record, x.rule));
|
|
18576
|
+
if (!isKept)
|
|
18577
|
+
return null;
|
|
18578
|
+
}
|
|
18579
|
+
} catch (error) {
|
|
18580
|
+
const err = new Error(`Consumer filter evaluation failed for consumer "${consumer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18581
|
+
Logger_default.error(err);
|
|
18582
|
+
throw err;
|
|
18497
18583
|
}
|
|
18498
|
-
|
|
18499
|
-
|
|
18500
|
-
|
|
18501
|
-
|
|
18584
|
+
try {
|
|
18585
|
+
if (requestOptions && requestOptions.filters) {
|
|
18586
|
+
const isKept = requestOptions.filters.every((x) => RequestExecutor_default.evaluateFilter(record, x));
|
|
18587
|
+
if (!isKept)
|
|
18588
|
+
return null;
|
|
18589
|
+
}
|
|
18590
|
+
} catch (error) {
|
|
18591
|
+
const err = new Error(`Request filter evaluation failed for consumer "${consumer.name}" (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
18592
|
+
Logger_default.error(err);
|
|
18593
|
+
throw err;
|
|
18502
18594
|
}
|
|
18503
18595
|
return record;
|
|
18504
18596
|
};
|
|
@@ -19175,6 +19267,9 @@ var ExecutorOrchestratorClass = class {
|
|
|
19175
19267
|
Logger_default.log(`[${usageId}] Spawning worker ${workerId} for producer "${prod.name}" \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
|
|
19176
19268
|
workerThreads.push(pool.exec("executor", [workerData], {
|
|
19177
19269
|
on: (payload) => this.onWorkAdvanced(payload, currentWorkerIndex, _progress)
|
|
19270
|
+
}).catch((error) => {
|
|
19271
|
+
Logger_default.error(error);
|
|
19272
|
+
return null;
|
|
19178
19273
|
}));
|
|
19179
19274
|
}
|
|
19180
19275
|
Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
|
|
@@ -19240,7 +19335,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
19240
19335
|
return finalResult;
|
|
19241
19336
|
} catch (error) {
|
|
19242
19337
|
Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
|
|
19243
|
-
Logger_default.error(
|
|
19338
|
+
Logger_default.error(error);
|
|
19244
19339
|
await pool.terminate();
|
|
19245
19340
|
await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
|
|
19246
19341
|
Logger_default.log(`[${usageId}] Running cleanup after failure`);
|