bulltrackers-module 1.0.226 → 1.0.228

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Main Orchestrator. Coordinates the topological execution.
3
- * UPDATED: Strict Dependency & Hash Cascade Logic with Explicit Failure Marking.
3
+ * UPDATED: Includes defensive checks for Logger compatibility.
4
4
  */
5
5
  const { normalizeName } = require('./utils/utils');
6
6
  const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
@@ -31,26 +31,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
31
31
  runnable: [],
32
32
  blocked: [], // Missing Root Data
33
33
  failedDependency: [], // Missing Dependency OR Stale Dependency
34
- reRuns: [], // Hash Mismatch (Triggered by self code change OR upstream cascade)
34
+ reRuns: [], // Hash Mismatch
35
35
  skipped: [] // Already done & valid
36
36
  };
37
37
 
38
- // Helper: Is a dependency satisfied AND valid (matching hash)?
39
38
  const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
40
39
  const norm = normalizeName(depName);
41
40
  const storedDepHash = dailyStatus[norm];
42
41
  const depManifest = manifestMap.get(norm);
43
42
 
44
- // 1. Must exist in DB. If missing, we cannot run.
45
43
  if (!storedDepHash) return false;
46
-
47
- // 2. Must exist in Manifest (Sanity check)
48
44
  if (!depManifest) return false;
49
-
50
- // 3. STRICT: The dependency's stored hash must match its current manifest hash.
51
- // If 'A' changed code, 'A' has a new hash. If we are running 'B' (Pass 2),
52
- // we expect 'A' (Pass 1) to have already run and updated the DB with the NEW hash.
53
- // If DB still has OLD hash, 'A' failed or didn't run. 'B' is unsafe to run.
54
45
  if (storedDepHash !== depManifest.hash) return false;
55
46
 
56
47
  return true;
@@ -61,7 +52,7 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
61
52
  const storedHash = dailyStatus[cName];
62
53
  const currentHash = calc.hash;
63
54
 
64
- // 1. Root Data Check (FATAL)
55
+ // 1. Root Data Check
65
56
  const missingRoots = [];
66
57
  if (calc.rootDataDependencies) {
67
58
  for (const dep of calc.rootDataDependencies) {
@@ -75,12 +66,10 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
75
66
 
76
67
  if (missingRoots.length > 0) {
77
68
  report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')}` });
78
- continue; // Cannot proceed
69
+ continue;
79
70
  }
80
71
 
81
- // 2. Dependency Check (FATAL)
82
- // Since we are in a topological pass system, all dependencies SHOULD be satisfied
83
- // by previous passes. If not, it is a fatal error for this calculation.
72
+ // 2. Dependency Check
84
73
  const missingDeps = [];
85
74
  if (calc.dependencies) {
86
75
  for (const dep of calc.dependencies) {
@@ -92,23 +81,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
92
81
 
93
82
  if (missingDeps.length > 0) {
94
83
  report.failedDependency.push({ name: cName, missing: missingDeps });
95
- continue; // Cannot proceed
84
+ continue;
96
85
  }
97
86
 
98
87
  // 3. Hash / State Check
99
88
  if (!storedHash) {
100
- // Case A: No result exists (or status is 'false' from previous failure) -> RUN
101
- // Note: If storedHash is boolean false, !storedHash is true, so we retry.
102
89
  report.runnable.push(calc);
103
90
  } else if (storedHash !== currentHash) {
104
- // Case B: Result exists, but hash mismatch -> RE-RUN
105
- // This covers code changes in THIS calc, AND cascading changes from dependencies
106
91
  report.reRuns.push({ name: cName, oldHash: storedHash, newHash: currentHash });
107
92
  } else if (storedHash === true) {
108
- // Case C: Legacy boolean status -> RE-RUN (Upgrade to hash)
109
93
  report.reRuns.push({ name: cName, reason: 'Legacy Upgrade' });
110
94
  } else {
111
- // Case D: Result exists, Hash Matches -> SKIP
112
95
  report.skipped.push({ name: cName });
113
96
  }
114
97
  }
@@ -119,13 +102,12 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
119
102
  async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
120
103
  const { logger } = dependencies;
121
104
  const orchestratorPid = generateProcessId(PROCESS_TYPES.ORCHESTRATOR, passToRun, dateStr);
122
-
123
105
  const dateToProcess = new Date(dateStr + 'T00:00:00Z');
124
106
 
125
107
  // 1. Fetch State
126
108
  const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
127
109
 
128
- // 2. Check Data Availability (One shot)
110
+ // 2. Check Data Availability
129
111
  const earliestDates = {
130
112
  portfolio: new Date('2025-09-25T00:00:00Z'),
131
113
  history: new Date('2025-11-05T00:00:00Z'),
@@ -141,11 +123,17 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
141
123
  const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
142
124
  const analysisReport = analyzeDateExecution(dateStr, calcsInThisPass, rootStatus, dailyStatus, manifestMap);
143
125
 
144
- // 4. LOG ANALYSIS
145
- logger.logDateAnalysis(dateStr, analysisReport);
126
+ // 4. LOG ANALYSIS (SAFEGUARDED)
127
+ if (logger && typeof logger.logDateAnalysis === 'function') {
128
+ logger.logDateAnalysis(dateStr, analysisReport);
129
+ } else {
130
+ // Fallback if injected logger is outdated
131
+ const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} | ReRuns: ${analysisReport.reRuns.length}`;
132
+ if (logger && logger.info) logger.info(logMsg);
133
+ else console.log(logMsg);
134
+ }
146
135
 
147
- // 5. MARK FAILURES (Explicitly write 'false' to DB for blocked items)
148
- // This prevents UI/downstream consumers from waiting indefinitely.
136
+ // 5. MARK FAILURES (Write 'false' to DB for blocked items)
149
137
  const failureUpdates = {};
150
138
  analysisReport.blocked.forEach(item => failureUpdates[item.name] = false);
151
139
  analysisReport.failedDependency.forEach(item => failureUpdates[item.name] = false);
@@ -163,11 +151,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
163
151
  const finalRunList = calcsInThisPass.filter(c => calcsToRunNames.has(normalizeName(c.name)));
164
152
 
165
153
  if (!finalRunList.length) {
166
- // Nothing to run (everything either skipped, blocked, or failed)
167
154
  return { date: dateStr, updates: {}, skipped: analysisReport.skipped.length };
168
155
  }
169
156
 
170
- logger.log('INFO', `[Orchestrator] Executing ${finalRunList.length} calculations for ${dateStr}`, { processId: orchestratorPid });
157
+ if (logger && logger.log) {
158
+ logger.log('INFO', `[Orchestrator] Executing ${finalRunList.length} calculations for ${dateStr}`, { processId: orchestratorPid });
159
+ }
171
160
 
172
161
  const standardToRun = finalRunList.filter(c => c.type === 'standard');
173
162
  const metaToRun = finalRunList.filter(c => c.type === 'meta');
@@ -177,11 +166,8 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
177
166
  try {
178
167
  const calcsRunning = [...standardToRun, ...metaToRun];
179
168
 
180
- // Fetch dependencies (Previous pass results)
181
- // includeSelf=false: We are re-running, so we ignore our own old results.
169
+ // Fetch dependencies
182
170
  const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
183
-
184
- // Fetch Yesterday's results (if Historical)
185
171
  const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
186
172
  const prevDateStr = prevDate.toISOString().slice(0, 10);
187
173
  const previousResults = await fetchExistingResults(prevDateStr, calcsRunning, computationManifest, config, dependencies, true);
@@ -196,9 +182,11 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
196
182
  }
197
183
 
198
184
  } catch (err) {
199
- // If execution CRASHES (code bug, timeout), we log and throw.
200
- // Pub/Sub will retry this message. This is correct for transient errors.
201
- logger.log('ERROR', `[Orchestrator] Failed execution for ${dateStr}`, { processId: orchestratorPid, error: err.message });
185
+ if (logger && logger.log) {
186
+ logger.log('ERROR', `[Orchestrator] Failed execution for ${dateStr}`, { processId: orchestratorPid, error: err.message });
187
+ } else {
188
+ console.error(`[Orchestrator] Failed execution for ${dateStr}: ${err.message}`);
189
+ }
202
190
  throw err;
203
191
  }
204
192
 
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * FILENAME: computation-system/helpers/computation_worker.js
3
3
  * PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
4
- * UPDATED: Fixed import source. Points to actual Calculations package, not layers.
4
+ * UPDATED: Instantiates the internal StructuredLogger to replace the generic injected logger.
5
5
  */
6
6
 
7
7
  const { runDateComputation, groupByPass } = require('../WorkflowOrchestrator.js');
8
8
  const { getManifest } = require('../topology/ManifestLoader');
9
+ const { StructuredLogger } = require('../logger/logger'); // <--- CRITICAL IMPORT
9
10
 
10
- // 1. IMPORT CALCULATIONS (CORRECTED)
11
+ // 1. IMPORT CALCULATIONS
11
12
  // We import the specific package containing your strategies (gem, pyro, core, etc.)
12
- // Adjust the require path if you are using a local relative path instead of node_modules.
13
13
  let calculationPackage;
14
14
  try {
15
15
  // Primary: Try to load from the installed npm package
@@ -21,34 +21,50 @@ try {
21
21
 
22
22
 
23
23
  // The package exports { calculations: { ... }, utils: { ... } }
24
- // We only need the 'calculations' object (which contains the folder-grouped classes)
25
24
  const calculations = calculationPackage.calculations;
26
25
 
27
26
  /**
28
27
  * Handles a single Pub/Sub message for a computation task.
29
28
  * Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
30
29
  * @param {object} message - The Pub/Sub message payload.
31
- * @param {object} config - System configuration (must include activeProductLines).
32
- * @param {object} dependencies - System dependencies (logger, db, etc.).
30
+ * @param {object} config - System configuration.
31
+ * @param {object} dependencies - Injected dependencies (db, generic logger, etc.).
33
32
  */
34
33
  async function handleComputationTask(message, config, dependencies) {
35
- const { logger } = dependencies;
34
+
35
+ // 2. INITIALIZE SYSTEM LOGGER
36
+ // We ignore the generic 'dependencies.logger' and instantiate our specialized one.
37
+ // This ensures methods like 'logDateAnalysis' and 'logStorage' exist.
38
+ const systemLogger = new StructuredLogger({
39
+ minLevel: config.minLevel || 'INFO',
40
+ enableStructured: true,
41
+ ...config // Apply any other relevant config overrides
42
+ });
36
43
 
37
- // 2. LAZY LOAD MANIFEST
38
- // We pass the CORRECT 'calculations' object here (e.g. { core: {...}, pyro: {...} })
44
+ // 3. OVERRIDE DEPENDENCIES
45
+ // We create a new dependencies object to pass downstream.
46
+ // This fixes the "logger.logStorage is not a function" error in ResultCommitter.js
47
+ const runDependencies = {
48
+ ...dependencies,
49
+ logger: systemLogger
50
+ };
51
+
52
+ const { logger } = runDependencies; // Use this for local logging
53
+
54
+ // 4. LAZY LOAD MANIFEST
39
55
  let computationManifest;
40
56
  try {
41
57
  computationManifest = getManifest(
42
58
  config.activeProductLines || [],
43
59
  calculations,
44
- dependencies
60
+ runDependencies // Pass the updated dependencies
45
61
  );
46
62
  } catch (manifestError) {
47
63
  logger.log('FATAL', `[Worker] Failed to load Manifest: ${manifestError.message}`);
48
64
  return;
49
65
  }
50
66
 
51
- // 3. PARSE PUB/SUB MESSAGE
67
+ // 5. PARSE PUB/SUB MESSAGE
52
68
  let data;
53
69
  try {
54
70
  if (message.data && message.data.message && message.data.message.data) {
@@ -67,7 +83,7 @@ async function handleComputationTask(message, config, dependencies) {
67
83
  return;
68
84
  }
69
85
 
70
- // 4. EXECUTE TASK
86
+ // 6. EXECUTE TASK
71
87
  try {
72
88
  if (!data || data.action !== 'RUN_COMPUTATION_DATE') {
73
89
  if (data) logger.log('WARN', `[Worker] Unknown or missing action: ${data?.action}. Ignoring.`);
@@ -95,7 +111,7 @@ async function handleComputationTask(message, config, dependencies) {
95
111
  pass,
96
112
  calcsInThisPass,
97
113
  config,
98
- dependencies,
114
+ runDependencies, // <--- IMPORTANT: Pass the dependencies with the System Logger
99
115
  computationManifest
100
116
  );
101
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.226",
3
+ "version": "1.0.228",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [