bulltrackers-module 1.0.358 → 1.0.360

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.
@@ -2,17 +2,12 @@
2
2
  * @fileoverview Core Firestore utility functions.
3
3
  * REFACTORED: All functions are now stateless and receive dependencies.
4
4
  * 'db' (Firestore instance) and 'logger' are passed via a 'dependencies' object.
5
+ * UPDATED: Added support for Popular Investor (Rankings) and Signed-In User fetching.
5
6
  */
6
7
  const { FieldValue, FieldPath } = require('@google-cloud/firestore');
7
8
 
8
9
  /**
9
10
  * Fetches and merges the most recent portfolio data for all normal users.
10
- * @param {object} dependencies - Contains db, logger.
11
- * @param {object} config - Configuration object.
12
- * @param {string} config.normalUserCollectionName - e.g., 'NormalUserPortfolios'.
13
- * @param {string} config.snapshotsSubCollectionName - e.g., 'snapshots'.
14
- * @param {string} config.partsSubCollectionName - e.g., 'parts'.
15
- * @returns {Promise<object>} A single object containing all user portfolios.
16
11
  */
17
12
  async function getLatestNormalUserPortfolios(dependencies, config) {
18
13
  const { db, logger } = dependencies;
@@ -34,10 +29,6 @@ async function getLatestNormalUserPortfolios(dependencies, config) {
34
29
 
35
30
  /**
36
31
  * Resets the proxy locks map in the performance document.
37
- * @param {object} dependencies - Contains db, logger.
38
- * @param {object} config - Configuration object.
39
- * @param {string} config.proxyPerformanceDocPath - e.g., 'system_state/proxy_performance'.
40
- * @returns {Promise<void>}
41
32
  */
42
33
  async function resetProxyLocks(dependencies, config) {
43
34
  const { db, logger } = dependencies;
@@ -55,12 +46,6 @@ async function resetProxyLocks(dependencies, config) {
55
46
 
56
47
  /**
57
48
  * Fetches and aggregates block capacities.
58
- * @param {object} dependencies - Contains db, logger.
59
- * @param {object} config - Configuration object.
60
- * @param {string} userType - 'normal' or 'speculator'.
61
- * @param {string} config.speculatorBlockCountsDocPath - Path to speculator counts doc.
62
- * @param {string} config.normalBlockCountsDocPath - Path to normal counts doc.
63
- * @returns {Promise<object>} Object containing counts.
64
49
  */
65
50
  async function getBlockCapacities(dependencies, config, userType) {
66
51
  const { db, logger } = dependencies;
@@ -78,14 +63,6 @@ async function getBlockCapacities(dependencies, config, userType) {
78
63
 
79
64
  /**
80
65
  * Fetches user IDs from multiple sources for exclusion lists.
81
- * @param {object} dependencies - Contains db, logger.
82
- * @param {object} config - Configuration object.
83
- * @param {string} userType - 'normal' or 'speculator'.
84
- * @param {string} config.specBlocksCollection - e.g., 'SpeculatorBlocks'.
85
- * @param {string} config.pendingSpecCollection - e.g., 'PendingSpeculators'.
86
- * @param {string} config.invalidSpecCollection - e.g., 'InvalidSpeculators'.
87
- * @param {Set<string>} config.existingNormalUserIds - PRE-FETCHED normal user IDs.
88
- * @returns {Promise<Set<string>>} A Set containing unique user IDs.
89
66
  */
90
67
  async function getExclusionIds(dependencies, config, userType) {
91
68
  const { db, logger } = dependencies;
@@ -95,15 +72,12 @@ async function getExclusionIds(dependencies, config, userType) {
95
72
  logger.log('TRACE', `[Core Utils] Loaded ${exclusionIds.size} existing normal user IDs for exclusion.`);
96
73
  const promises = [];
97
74
  try {
98
- // 1. Existing Speculators
99
75
  const specBlocksRef = db.collection(specBlocksCollection);
100
76
  promises.push(specBlocksRef.get().then(snapshot => {snapshot.forEach(doc => {const users = doc.data().users || {}; Object.keys(users).forEach(key => exclusionIds.add(key.split('.')[1])); });
101
77
  logger.log('TRACE','[Core Utils] Fetched existing speculator IDs for exclusion.');}));
102
- // 2. Pending Speculators
103
78
  if (userType === 'speculator') {const pendingRef = db.collection(pendingSpecCollection);
104
79
  promises.push(pendingRef.get().then(snapshot => {snapshot.forEach(doc => {Object.keys(doc.data().users || {}).forEach(cid => exclusionIds.add(cid));});
105
80
  logger.log('TRACE','[Core Utils] Fetched pending speculator IDs for exclusion.');})); }
106
- // 3. Invalid Speculators
107
81
  const invalidRef = db.collection(invalidSpecCollection);
108
82
  promises.push(invalidRef.get().then(snapshot => { snapshot.forEach(doc => {const data = doc.data();if (data) {Object.keys(data.users || {}).forEach(cid => exclusionIds.add(cid));}});
109
83
  logger.log('TRACE','[Core Utils] Fetched invalid speculator IDs for exclusion.');}));
@@ -117,11 +91,6 @@ async function getExclusionIds(dependencies, config, userType) {
117
91
 
118
92
  /**
119
93
  * Scans normal user portfolios for potential speculator candidates.
120
- * @param {object} dependencies - Contains db, logger.
121
- * @param {Set<string>} exclusionIds - IDs to exclude.
122
- * @param {Set<number>} speculatorInstrumentSet - Set of instrument IDs.
123
- * @param {object} latestNormalPortfolios - PRE-FETCHED portfolio object.
124
- * @returns {Promise<string[]>} Array of prioritized speculator CIDs.
125
94
  */
126
95
  async function getPrioritizedSpeculators(dependencies, exclusionIds, speculatorInstrumentSet, latestNormalPortfolios) {
127
96
  const { logger } = dependencies;
@@ -143,10 +112,6 @@ async function getPrioritizedSpeculators(dependencies, exclusionIds, speculatorI
143
112
 
144
113
  /**
145
114
  * Deletes all documents within a specified collection path.
146
- * @param {object} dependencies - Contains db, logger.
147
- * @param {string} collectionPath - The path to the collection to clear.
148
- * @param {number} maxBatchSize - Firestore batch size limit.
149
- * @returns {Promise<void>}
150
115
  */
151
116
  async function clearCollection(dependencies, collectionPath, maxBatchSize = 400) {
152
117
  const { db, logger } = dependencies;
@@ -174,14 +139,6 @@ async function clearCollection(dependencies, collectionPath, maxBatchSize = 400)
174
139
 
175
140
  /**
176
141
  * Writes an array of user IDs into sharded Firestore documents.
177
- * @param {object} dependencies - Contains db, logger.
178
- * @param {object} config - Configuration object.
179
- * @param {string} config.collectionPath - Base path for sharded documents (e.g., 'PendingSpeculators').
180
- * @param {string[]} config.items - The user IDs to write.
181
- * @param {Date} config.timestamp - Timestamp to associate with each user ID.
182
- * @param {number} config.maxFieldsPerDoc - Max user IDs per sharded document.
183
- * @param {number} config.maxWritesPerBatch - Max documents to write per batch commit.
184
- * @returns {Promise<void>}
185
142
  */
186
143
  async function batchWriteShardedIds(dependencies, config) {
187
144
  const { db, logger } = dependencies;
@@ -219,12 +176,6 @@ async function batchWriteShardedIds(dependencies, config) {
219
176
 
220
177
  /**
221
178
  * Gets normal users whose timestamp indicates they need updating.
222
- * @param {object} dependencies - Contains db, logger.
223
- * @param {object} config - Configuration object.
224
- * @param {Date} config.dateThreshold - Users processed before this date will be returned.
225
- * @param {string} config.normalUserCollectionName - e.g., 'NormalUserPortfolios'.
226
- * @param {string} config.normalUserTimestampsDocId - e.g., 'normal'.
227
- * @returns {Promise<string[]>} Array of user IDs to update.
228
179
  */
229
180
  async function getNormalUsersToUpdate(dependencies, config) {
230
181
  const { db, logger } = dependencies;
@@ -248,12 +199,6 @@ async function getNormalUsersToUpdate(dependencies, config) {
248
199
 
249
200
  /**
250
201
  * Gets speculator users/instruments whose data needs updating.
251
- * @param {object} dependencies - Contains db, logger.
252
- * @param {object} config - Configuration object.
253
- * @param {Date} config.dateThreshold - Verification date threshold.
254
- * @param {Date} config.gracePeriodThreshold - Last held asset date threshold.
255
- * @param {string} config.speculatorBlocksCollectionName - e.g., 'SpeculatorBlocks'.
256
- * @returns {Promise<object[]>} Array of objects like { userId: string, instrumentId: number }.
257
202
  */
258
203
  async function getSpeculatorsToUpdate(dependencies, config) {
259
204
  const { db, logger } = dependencies;
@@ -281,4 +226,93 @@ async function getSpeculatorsToUpdate(dependencies, config) {
281
226
  } catch (error) { logger.log('ERROR','[Core Utils] Error getting speculators to update', { errorMessage: error.message }); throw error; }
282
227
  }
283
228
 
284
- module.exports = { getLatestNormalUserPortfolios, resetProxyLocks, getBlockCapacities, getExclusionIds, getPrioritizedSpeculators, clearCollection, batchWriteShardedIds, getNormalUsersToUpdate, getSpeculatorsToUpdate, };
229
+ /**
230
+ * [NEW] Fetches Popular Investors from the daily rankings document.
231
+ */
232
+ async function getPopularInvestorsToUpdate(dependencies, config) {
233
+ const { db, logger } = dependencies;
234
+ const collectionName = config.popularInvestorRankingsCollection || process.env.FIRESTORE_COLLECTION_PI_RANKINGS || 'popular_investor_rankings';
235
+
236
+ logger.log('INFO', `[Core Utils] Getting Popular Investors to update from ${collectionName}...`);
237
+
238
+ // Construct today's document ID (YYYY-MM-DD)
239
+ const today = new Date().toISOString().split('T')[0];
240
+ const docPath = `${collectionName}/${today}`;
241
+
242
+ try {
243
+ const docRef = db.doc(docPath);
244
+ const docSnap = await docRef.get();
245
+
246
+ if (!docSnap.exists) {
247
+ logger.log('WARN', `[Core Utils] No Popular Investor rankings found for date: ${today} at ${docPath}`);
248
+ return [];
249
+ }
250
+
251
+ const data = docSnap.data();
252
+ const items = data.Items || [];
253
+
254
+ // Map to { cid, username }
255
+ const targets = items.map(item => ({
256
+ cid: String(item.CustomerId),
257
+ username: item.UserName
258
+ }));
259
+
260
+ logger.log('INFO', `[Core Utils] Found ${targets.length} Popular Investors from ${today} ranking.`);
261
+ return targets;
262
+
263
+ } catch (error) {
264
+ logger.log('ERROR', '[Core Utils] Error getting Popular Investors', { errorMessage: error.message });
265
+ throw error;
266
+ }
267
+ }
268
+
269
+ /**
270
+ * [NEW] Fetches all Signed-In Users for daily update.
271
+ */
272
+ async function getSignedInUsersToUpdate(dependencies, config) {
273
+ const { db, logger } = dependencies;
274
+ const collectionName = config.signedInUsersCollection || process.env.FIRESTORE_COLLECTION_SIGNED_IN_USER_PORTFOLIOS || 'signed_in_users';
275
+
276
+ logger.log('INFO', `[Core Utils] Getting Signed-In Users to update from ${collectionName}...`);
277
+
278
+ try {
279
+ const snapshot = await db.collection(collectionName).get();
280
+
281
+ if (snapshot.empty) {
282
+ logger.log('INFO', '[Core Utils] No Signed-In Users found.');
283
+ return [];
284
+ }
285
+
286
+ const targets = [];
287
+ snapshot.forEach(doc => {
288
+ const data = doc.data();
289
+ // Assuming the doc ID is the CID, or it's a field 'cid'
290
+ const cid = data.cid || doc.id;
291
+ const username = data.username || 'Unknown';
292
+ // Only push if we have a valid CID
293
+ if(cid) targets.push({ cid: String(cid), username });
294
+ });
295
+
296
+ logger.log('INFO', `[Core Utils] Found ${targets.length} Signed-In Users.`);
297
+ return targets;
298
+
299
+ } catch (error) {
300
+ logger.log('ERROR', '[Core Utils] Error getting Signed-In Users', { errorMessage: error.message });
301
+ throw error;
302
+ }
303
+ }
304
+
305
+ module.exports = {
306
+ getLatestNormalUserPortfolios,
307
+ resetProxyLocks,
308
+ getBlockCapacities,
309
+ getExclusionIds,
310
+ getPrioritizedSpeculators,
311
+ clearCollection,
312
+ batchWriteShardedIds,
313
+ getNormalUsersToUpdate,
314
+ getSpeculatorsToUpdate,
315
+ // New Exports
316
+ getPopularInvestorsToUpdate,
317
+ getSignedInUsersToUpdate
318
+ };
@@ -5,7 +5,7 @@
5
5
 
6
6
  /**
7
7
  * Sub-pipe: pipe.orchestrator.getUpdateTargets
8
- * @param {string} userType - 'normal' or 'speculator'.
8
+ * @param {string} userType - 'normal', 'speculator', 'popular_investor', or 'signed_in_user'.
9
9
  * @param {object} thresholds - Contains date thresholds.
10
10
  * @param {object} config - The *global* updateConfig object.
11
11
  * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
@@ -16,24 +16,39 @@ async function getUpdateTargets(userType, thresholds, config, dependencies) {
16
16
  logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
17
17
  let targets = [];
18
18
  try {
19
- if (userType === 'normal') { targets = await firestoreUtils.getNormalUsersToUpdate(dependencies,{
20
- dateThreshold: thresholds.dateThreshold,
21
- normalUserCollectionName: config.normalUserCollectionName,
22
- normalUserTimestampsDocId: config.normalUserTimestampsDocId
23
- }); }
24
- else if (userType === 'speculator') { targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies,{
25
- dateThreshold: thresholds.dateThreshold,
26
- gracePeriodThreshold: thresholds.gracePeriodThreshold,
27
- speculatorBlocksCollectionName: config.speculatorBlocksCollectionName}); }
28
- logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
29
- return targets;}
30
- catch (error) { logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message }); throw error; }
19
+ if (userType === 'normal') {
20
+ targets = await firestoreUtils.getNormalUsersToUpdate(dependencies,{
21
+ dateThreshold: thresholds.dateThreshold,
22
+ normalUserCollectionName: config.normalUserCollectionName,
23
+ normalUserTimestampsDocId: config.normalUserTimestampsDocId
24
+ });
25
+ }
26
+ else if (userType === 'speculator') {
27
+ targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies,{
28
+ dateThreshold: thresholds.dateThreshold,
29
+ gracePeriodThreshold: thresholds.gracePeriodThreshold,
30
+ speculatorBlocksCollectionName: config.speculatorBlocksCollectionName
31
+ });
32
+ }
33
+ else if (userType === 'popular_investor') {
34
+ targets = await firestoreUtils.getPopularInvestorsToUpdate(dependencies, config);
35
+ }
36
+ else if (userType === 'signed_in_user') {
37
+ targets = await firestoreUtils.getSignedInUsersToUpdate(dependencies, config);
38
+ }
39
+
40
+ logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
41
+ return targets;
42
+ } catch (error) {
43
+ logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message });
44
+ throw error;
45
+ }
31
46
  }
32
47
 
33
48
  /**
34
49
  * Sub-pipe: pipe.orchestrator.dispatchUpdates
35
50
  * @param {Array<any>} targets - Array of targets from getUpdateTargets.
36
- * @param {string} userType - 'normal' or 'speculator'.
51
+ * @param {string} userType - 'normal', 'speculator', 'popular_investor', or 'signed_in_user'.
37
52
  * @param {object} config - The *global* updateConfig object.
38
53
  * @param {object} dependencies - Contains db, pubsub, logger, pubsubUtils.
39
54
  * @returns {Promise<void>}
@@ -41,11 +56,43 @@ async function getUpdateTargets(userType, thresholds, config, dependencies) {
41
56
  async function dispatchUpdates(targets, userType, config, dependencies) {
42
57
  const { logger, pubsubUtils } = dependencies;
43
58
  const { dispatcherTopicName, taskBatchSize, pubsubBatchSize } = config;
44
- if (targets.length === 0) { logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`); return; }
45
- logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
46
- const individualTasks = targets.map(target => ({ type: 'update', userId: userType === 'normal' ? target : target.userId, instruments: userType === 'speculator' ? target.instruments : undefined, userType })); const dispatcherMessages = [];
47
- for (let i = 0; i < individualTasks.length; i += taskBatchSize) { dispatcherMessages.push({ tasks: individualTasks.slice(i, i + taskBatchSize) }); }
48
- try { await pubsubUtils.batchPublishTasks(dependencies,{ topicName: dispatcherTopicName, tasks: dispatcherMessages, taskType: `${userType} update batch`, maxPubsubBatchSize: pubsubBatchSize }); logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`); }
49
- catch (error) { logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message }); throw error; }}
59
+
60
+ if (targets.length === 0) {
61
+ logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`);
62
+ return;
63
+ }
64
+
65
+ logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
66
+
67
+ const individualTasks = targets.map(target => {
68
+ if (userType === 'normal') {
69
+ return { type: 'update', userId: target, userType };
70
+ } else if (userType === 'speculator') {
71
+ return { type: 'update', userId: target.userId, instruments: target.instruments, userType };
72
+ } else if (userType === 'popular_investor') {
73
+ return { type: 'POPULAR_INVESTOR_UPDATE', cid: target.cid, username: target.username };
74
+ } else if (userType === 'signed_in_user') {
75
+ return { type: 'ON_DEMAND_USER_UPDATE', cid: target.cid, username: target.username };
76
+ }
77
+ });
50
78
 
51
- module.exports = { getUpdateTargets, dispatchUpdates };
79
+ const dispatcherMessages = [];
80
+ for (let i = 0; i < individualTasks.length; i += taskBatchSize) {
81
+ dispatcherMessages.push({ tasks: individualTasks.slice(i, i + taskBatchSize) });
82
+ }
83
+
84
+ try {
85
+ await pubsubUtils.batchPublishTasks(dependencies,{
86
+ topicName: dispatcherTopicName,
87
+ tasks: dispatcherMessages,
88
+ taskType: `${userType} update batch`,
89
+ maxPubsubBatchSize: pubsubBatchSize
90
+ });
91
+ logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`);
92
+ } catch (error) {
93
+ logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message });
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ module.exports = { getUpdateTargets, dispatchUpdates };
@@ -21,8 +21,34 @@ async function runUpdateOrchestrator(config, deps) {
21
21
  const { logger, firestoreUtils } = deps;
22
22
  logger.log('INFO', '🚀 Update Orchestrator triggered...');
23
23
  await firestoreUtils.resetProxyLocks(deps, config);
24
+
25
+ // 1. Normal Users
24
26
  await runUpdates('normal', config.updateConfig, config, deps);
27
+
28
+ // 2. Speculators
25
29
  await runUpdates('speculator', config.updateConfig, config, deps);
30
+
31
+ // 3. Popular Investors
32
+ try {
33
+ const piConfig = {
34
+ ...config.updateConfig,
35
+ popularInvestorRankingsCollection: config.updateConfig.popularInvestorRankingsCollection || process.env.FIRESTORE_COLLECTION_PI_RANKINGS || 'popular_investor_rankings'
36
+ };
37
+ await runUpdates('popular_investor', piConfig, config, deps);
38
+ } catch (e) {
39
+ logger.log('ERROR', '[Orchestrator] Failed to run Popular Investor updates.', e);
40
+ }
41
+
42
+ // 4. Signed-In Users
43
+ try {
44
+ const signedInConfig = {
45
+ ...config.updateConfig,
46
+ signedInUsersCollection: config.updateConfig.signedInUsersCollection || process.env.FIRESTORE_COLLECTION_SIGNED_IN_USER_PORTFOLIOS || 'signed_in_users'
47
+ };
48
+ await runUpdates('signed_in_user', signedInConfig, config, deps);
49
+ } catch (e) {
50
+ logger.log('ERROR', '[Orchestrator] Failed to run Signed-In User updates.', e);
51
+ }
26
52
  }
27
53
 
28
54
  /** Stage 3: Run discovery for a single user type */
@@ -61,4 +87,4 @@ async function runUpdates(userType, updateConfig, globalConfig, deps) {
61
87
  logger.log('SUCCESS', `[Orchestrator] Dispatched update tasks for ${userType}.`);
62
88
  }
63
89
 
64
- module.exports = { runDiscoveryOrchestrator, runUpdateOrchestrator, runDiscovery, runUpdates };
90
+ module.exports = { runDiscoveryOrchestrator, runUpdateOrchestrator, runDiscovery, runUpdates };
@@ -1,19 +1,14 @@
1
1
  /**
2
2
  * @fileoverview Main entry point for the Task Engine Cloud Function.
3
- * Routes incoming Pub/Sub messages to the appropriate helper functions.
4
3
  */
5
4
  const { handleDiscover } = require('./helpers/discover_helpers');
6
5
  const { handleVerify } = require('./helpers/verify_helpers');
7
- const { handleUpdate } = require('./helpers/update_helpers'); // Existing logic for "Speculators"
6
+ const { handleUpdate } = require('./helpers/update_helpers');
8
7
  const { handlePopularInvestorUpdate, handleOnDemandUserUpdate } = require('./helpers/popular_investor_helpers');
9
8
 
10
- /**
11
- * Handles the incoming request from Pub/Sub or Direct HTTP.
12
- * @param {object} message - The Pub/Sub message object (or req.body).
13
- * @param {object} context - The event context.
14
- * @param {object} config - The Task Engine configuration.
15
- * @param {object} dependencies - Injected dependencies (db, logger, proxyManager, etc.).
16
- */
9
+ // IMPORT THE UTILS TO HANDLE BATCHES
10
+ const { executeTasks, prepareTaskBatches } = require('./utils/task_engine_utils');
11
+
17
12
  async function handleRequest(message, context, config, dependencies) {
18
13
  const { logger } = dependencies;
19
14
 
@@ -27,12 +22,27 @@ async function handleRequest(message, context, config, dependencies) {
27
22
  return;
28
23
  }
29
24
 
25
+ // --- FIX START: Handle Batch vs Single ---
26
+
27
+ // CASE A: Payload is a Batch (from Dispatcher)
28
+ if (payload.tasks && Array.isArray(payload.tasks)) {
29
+ logger.log('INFO', `[TaskEngine] Received BATCH of ${payload.tasks.length} tasks.`);
30
+ const taskId = context.eventId || 'batch-' + Date.now();
31
+
32
+ // Use existing utils to execute the batch
33
+ const { tasksToRun, otherTasks } = await prepareTaskBatches(payload.tasks, null, logger);
34
+ await executeTasks(tasksToRun, otherTasks, dependencies, config, taskId);
35
+ return;
36
+ }
37
+
38
+ // CASE B: Payload is a Single Task (from Cron/On-Demand)
30
39
  const { type, data } = payload;
31
40
 
32
41
  if (!type) {
33
42
  logger.log('WARN', '[TaskEngine] Received message with no type.', payload);
34
43
  return;
35
44
  }
45
+ // --- FIX END ---
36
46
 
37
47
  logger.log('INFO', `[TaskEngine] Processing Task: ${type}`, { dataSummary: JSON.stringify(data).substring(0, 100) });
38
48
 
@@ -40,23 +50,18 @@ async function handleRequest(message, context, config, dependencies) {
40
50
  try {
41
51
  switch (type) {
42
52
  case 'DISCOVER':
43
- await handleDiscover(data, config, dependencies);
53
+ await handleDiscover(data, 'single-discover', dependencies, config); // Added taskId arg
44
54
  break;
45
55
  case 'VERIFY':
46
- await handleVerify(data, config, dependencies);
56
+ await handleVerify(data, 'single-verify', dependencies, config); // Added taskId arg
47
57
  break;
48
58
  case 'UPDATE':
49
- // Existing logic for "Speculator" updates (high volume, low fidelity)
50
- await handleUpdate(data, config, dependencies);
59
+ await handleUpdate(data, 'single-update', dependencies, config); // Added taskId arg
51
60
  break;
52
61
  case 'POPULAR_INVESTOR_UPDATE':
53
- // New logic for Popular Investors (daily cron, high fidelity)
54
- // data should contain { cid, username }
55
62
  await handlePopularInvestorUpdate(data, config, dependencies);
56
63
  break;
57
64
  case 'ON_DEMAND_USER_UPDATE':
58
- // New logic for Signed-In Users (immediate trigger, cost optimized)
59
- // data should contain { cid, username }
60
65
  await handleOnDemandUserUpdate(data, config, dependencies);
61
66
  break;
62
67
  default:
@@ -64,8 +69,6 @@ async function handleRequest(message, context, config, dependencies) {
64
69
  }
65
70
  } catch (err) {
66
71
  logger.log('ERROR', `[TaskEngine] Error processing task ${type}`, err);
67
- // We might want to throw here to trigger Pub/Sub retry, depending on the error nature
68
- // For now, logging error is sufficient as the sub-handlers perform their own try/catch blocks
69
72
  }
70
73
  }
71
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.358",
3
+ "version": "1.0.360",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [