bulltrackers-module 1.0.201 → 1.0.203

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,10 +1,13 @@
1
1
  /**
2
2
  * FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_dispatcher.js
3
3
  * PURPOSE: Dispatches computation tasks to Pub/Sub for scalable execution.
4
+ * FIXED: Instantiates PubSubUtils locally to ensure valid logger/dependencies are used.
5
+ * IMPROVED: Logging now explicitly lists the calculations being scheduled.
4
6
  */
5
7
 
6
8
  const { getExpectedDateStrings } = require('../utils/utils.js');
7
9
  const { groupByPass } = require('./orchestration_helpers.js');
10
+ const { PubSubUtils } = require('../../core/utils/pubsub_utils');
8
11
 
9
12
  const TOPIC_NAME = 'computation-tasks';
10
13
 
@@ -13,16 +16,30 @@ const TOPIC_NAME = 'computation-tasks';
13
16
  * Instead of running them, it queues them in Pub/Sub.
14
17
  */
15
18
  async function dispatchComputationPass(config, dependencies, computationManifest) {
16
- const { logger, pubsubUtils } = dependencies;
19
+ const { logger } = dependencies;
20
+
21
+ // Create fresh PubSubUtils instance
22
+ const pubsubUtils = new PubSubUtils(dependencies);
23
+
17
24
  const passToRun = String(config.COMPUTATION_PASS_TO_RUN);
18
25
 
19
26
  if (!passToRun) {
20
27
  return logger.log('ERROR', '[Dispatcher] No pass defined (COMPUTATION_PASS_TO_RUN). Aborting.');
21
28
  }
22
29
 
23
- logger.log('INFO', `🚀 [Dispatcher] Preparing to dispatch PASS ${passToRun}...`);
30
+ // 1. Validate Pass Existence
31
+ const passes = groupByPass(computationManifest);
32
+ const calcsInThisPass = passes[passToRun] || [];
33
+
34
+ if (!calcsInThisPass.length) {
35
+ return logger.log('WARN', `[Dispatcher] No calcs for Pass ${passToRun}. Exiting.`);
36
+ }
37
+
38
+ const calcNames = calcsInThisPass.map(c => c.name).join(', ');
39
+ logger.log('INFO', `🚀 [Dispatcher] Preparing PASS ${passToRun}.`);
40
+ logger.log('INFO', `[Dispatcher] Included Calculations: [${calcNames}]`);
24
41
 
25
- // 1. Determine Date Range (Same logic as PassRunner)
42
+ // 2. Determine Date Range
26
43
  // Hardcoded earliest dates - keep synced with PassRunner for now
27
44
  const earliestDates = {
28
45
  portfolio: new Date('2025-09-25T00:00:00Z'),
@@ -36,19 +53,11 @@ async function dispatchComputationPass(config, dependencies, computationManifest
36
53
 
37
54
  const allExpectedDates = getExpectedDateStrings(passEarliestDate, endDateUTC);
38
55
 
39
- // 2. Validate Pass Existence
40
- const passes = groupByPass(computationManifest);
41
- const calcsInThisPass = passes[passToRun] || [];
42
-
43
- if (!calcsInThisPass.length) {
44
- return logger.log('WARN', `[Dispatcher] No calcs for Pass ${passToRun}. Exiting.`);
45
- }
46
-
47
- logger.log('INFO', `[Dispatcher] Found ${calcsInThisPass.length} calcs for Pass ${passToRun}. Target dates: ${allExpectedDates.length}`);
56
+ logger.log('INFO', `[Dispatcher] Dispatches checks for ${allExpectedDates.length} dates (${allExpectedDates[0]} to ${allExpectedDates[allExpectedDates.length - 1]}). Workers will validate dependencies.`);
48
57
 
49
58
  // 3. Dispatch Messages
50
59
  let dispatchedCount = 0;
51
- const BATCH_SIZE = 50; // Pub/Sub batch publishing size
60
+ const BATCH_SIZE = 50;
52
61
 
53
62
  // We can publish in parallel batches
54
63
  const chunks = [];
@@ -75,8 +84,8 @@ async function dispatchComputationPass(config, dependencies, computationManifest
75
84
  }
76
85
  }
77
86
 
78
- logger.log('INFO', `[Dispatcher] Finished dispatching. Total tasks: ${dispatchedCount}`);
87
+ logger.log('INFO', `[Dispatcher] Finished. Dispatched ${dispatchedCount} checks for Pass ${passToRun}.`);
79
88
  return { dispatched: dispatchedCount };
80
89
  }
81
90
 
82
- module.exports = { dispatchComputationPass };
91
+ module.exports = { dispatchComputationPass };
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_worker.js
3
3
  * PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
4
+ * FIXED: Added robust payload parsing to handle Cloud Functions Gen 2 (CloudEvents).
4
5
  */
5
6
 
6
7
  const { runDateComputation } = require('./computation_pass_runner.js');
@@ -8,15 +9,43 @@ const { groupByPass } = require('./orchestration_helpers.js');
8
9
 
9
10
  /**
10
11
  * Handles a single Pub/Sub message for a computation task.
12
+ * Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
11
13
  */
12
14
  async function handleComputationTask(message, config, dependencies, computationManifest) {
13
15
  const { logger } = dependencies;
14
16
 
17
+ let data;
15
18
  try {
16
- const data = message.json || message; // Handle both raw payload and parsed JSON
19
+ // 1. Handle Cloud Functions Gen 2 (CloudEvent)
20
+ // Structure: event.data.message.data (base64)
21
+ if (message.data && message.data.message && message.data.message.data) {
22
+ const buffer = Buffer.from(message.data.message.data, 'base64');
23
+ data = JSON.parse(buffer.toString());
24
+ }
25
+ // 2. Handle Cloud Functions Gen 1 / Legacy PubSub
26
+ // Structure: message.data (base64) or message.json
27
+ else if (message.data && typeof message.data === 'string') {
28
+ const buffer = Buffer.from(message.data, 'base64');
29
+ data = JSON.parse(buffer.toString());
30
+ }
31
+ // 3. Handle Direct JSON (Test harness or simulator)
32
+ else if (message.json) {
33
+ data = message.json;
34
+ }
35
+ // 4. Fallback: Assume message is the payload
36
+ else {
37
+ data = message;
38
+ }
39
+ } catch (parseError) {
40
+ logger.log('ERROR', `[Worker] Failed to parse Pub/Sub payload.`, { error: parseError.message });
41
+ return;
42
+ }
17
43
 
18
- if (data.action !== 'RUN_COMPUTATION_DATE') {
19
- logger.log('WARN', `[Worker] Unknown action: ${data.action}. Ignoring.`);
44
+ try {
45
+ // Validate Action
46
+ if (!data || data.action !== 'RUN_COMPUTATION_DATE') {
47
+ // Only log if data exists but action is wrong, prevents log spam on empty messages
48
+ if (data) logger.log('WARN', `[Worker] Unknown or missing action: ${data?.action}. Ignoring.`);
20
49
  return;
21
50
  }
22
51
 
@@ -39,12 +68,13 @@ async function handleComputationTask(message, config, dependencies, computationM
39
68
  }
40
69
 
41
70
  // Execute the computation for this specific date
71
+ // The runner internally checks dependencies (Pass 1, 2, 3 status) and skips if not ready.
42
72
  const result = await runDateComputation(date, pass, calcsInThisPass, config, dependencies, computationManifest);
43
73
 
44
74
  if (result) {
45
75
  logger.log('INFO', `[Worker] Successfully processed ${date} (Pass ${pass}). Updates: ${Object.keys(result.updates || {}).length}`);
46
76
  } else {
47
- logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) - No action needed or data missing.`);
77
+ logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) - Skipped (Dependencies missing or already done).`);
48
78
  }
49
79
 
50
80
  } catch (err) {
@@ -53,4 +83,4 @@ async function handleComputationTask(message, config, dependencies, computationM
53
83
  }
54
84
  }
55
85
 
56
- module.exports = { handleComputationTask };
86
+ module.exports = { handleComputationTask };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.201",
3
+ "version": "1.0.203",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [