bulltrackers-module 1.0.209 → 1.0.211

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,10 +2,12 @@
2
2
  * @fileoverview Main pipe for the Task Engine.
3
3
  * REFACTORED: This function is the high-level "manual of processes"
4
4
  * that orchestrates the task engine logic.
5
+ * FIXED: Removed dead call to runUsernameLookups.
5
6
  */
6
7
 
7
8
  // Import the new utility functions that contain the logic
8
- const { parseTaskPayload, prepareTaskBatches, runUsernameLookups, executeTasks } = require('./utils/task_engine_utils');
9
+ // REMOVED: runUsernameLookups from import
10
+ const { parseTaskPayload, prepareTaskBatches, executeTasks } = require('./utils/task_engine_utils');
9
11
 
10
12
  /**
11
13
  * Main pipe: pipe.taskEngine.handleRequest
@@ -20,15 +22,29 @@ async function handleRequest(message, context, config, dependencies) {
20
22
  const taskId = `batch-${context.eventId || Date.now()}`;
21
23
  try {
22
24
  const tasks = parseTaskPayload(message, logger);
23
- if (!tasks) {return; }
25
+ if (!tasks) { return; }
26
+
24
27
  logger.log('INFO', `[TaskEngine/${taskId}] Received batch of ${tasks.length} tasks.`);
25
- const { tasksToRun, cidsToLookup, otherTasks } = await prepareTaskBatches(tasks, batchManager, logger);
26
- await runUsernameLookups(tasksToRun, cidsToLookup, dependencies, config, batchManager, logger);
28
+
29
+ // Prepare batches
30
+ const { tasksToRun, otherTasks } = await prepareTaskBatches(tasks, batchManager, logger);
31
+
32
+ // REMOVED: await runUsernameLookups(...) which caused the crash
33
+
34
+ // Execute (Now runs everything sequentially inside executeTasks)
27
35
  await executeTasks(tasksToRun, otherTasks, dependencies, config, taskId);
28
- } catch (error) {logger.log('ERROR', `[TaskEngine/${taskId}] Failed during batch processing.`, { errorMessage: error.message, errorStack: error.stack });
29
- } finally { try {logger.log('INFO', `[TaskEngine/${taskId}] Flushing all accumulated batches...`);
30
- await batchManager.flushBatches();
31
- logger.log('INFO', `[TaskEngine/${taskId}] Final batch and header flush complete.`);} catch (flushError) {logger.log('ERROR', `[TaskEngine/${taskId}] Error during final flush attempt.`, { error: flushError.message });}}
36
+
37
+ } catch (error) {
38
+ logger.log('ERROR', `[TaskEngine/${taskId}] Failed during batch processing.`, { errorMessage: error.message, errorStack: error.stack });
39
+ } finally {
40
+ try {
41
+ logger.log('INFO', `[TaskEngine/${taskId}] Flushing all accumulated batches...`);
42
+ await batchManager.flushBatches();
43
+ logger.log('INFO', `[TaskEngine/${taskId}] Final batch and header flush complete.`);
44
+ } catch (flushError) {
45
+ logger.log('ERROR', `[TaskEngine/${taskId}] Error during final flush attempt.`, { error: flushError.message });
46
+ }
47
+ }
32
48
  }
33
49
 
34
- module.exports = {handleRequest};
50
+ module.exports = { handleRequest };
@@ -4,6 +4,8 @@
4
4
  * (OPTIMIZED V3: Removed obsolete username lookup logic)
5
5
  * (OPTIMIZED V2: Added "Circuit Breaker" for Proxy failures)
6
6
  * (REFACTORED: Concurrency set to 1, added fallback and verbose logging)
7
+ * (FIXED: Improved logging clarity for Normal vs Speculator users)
8
+ * (FIXED: Final log now accurately reflects failure state)
7
9
  */
8
10
 
9
11
  const { FieldValue } = require('@google-cloud/firestore');
@@ -66,6 +68,8 @@ function detectSpeculatorTargets(historyData, portfolioData) {
66
68
  */
67
69
  async function handleUpdate(task, taskId, { logger, headerManager, proxyManager, db, batchManager, pubsub }, config) {
68
70
  const { userId, instruments, instrumentId, userType } = task;
71
+
72
+ // Normalize the loop: Speculators get specific IDs, Normal users get [undefined] to trigger one pass.
69
73
  const instrumentsToProcess = userType === 'speculator' ? (instruments || [instrumentId]) : [undefined];
70
74
  const today = new Date().toISOString().slice(0, 10);
71
75
  const portfolioBlockId = `${Math.floor(parseInt(userId) / 1000000)}M`;
@@ -75,7 +79,12 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
75
79
  let capturedHistory = null;
76
80
  let capturedPortfolio = null;
77
81
 
78
- logger.log('TRACE', `[handleUpdate/${userId}] Starting update task. Type: ${userType}. Instruments: ${instrumentsToProcess.join(', ')}`);
82
+ // Track overall success for the final log
83
+ let hasPortfolioErrors = false;
84
+
85
+ // FIX 1: Better Start Log
86
+ const scopeLog = userType === 'speculator' ? `Instruments: [${instrumentsToProcess.join(', ')}]` : 'Scope: Full Portfolio';
87
+ logger.log('TRACE', `[handleUpdate/${userId}] Starting update task. Type: ${userType}. ${scopeLog}`);
79
88
 
80
89
  // --- 1. Process History Fetch (Sequentially) ---
81
90
  let historyHeader = null;
@@ -148,13 +157,16 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
148
157
  logger.log('TRACE', `[handleUpdate/${userId}] Starting ${instrumentsToProcess.length} sequential portfolio fetches.`);
149
158
 
150
159
  for (const instId of instrumentsToProcess) {
160
+ // FIX 2: Define a clear scope name for logging
161
+ const scopeName = instId ? `Instrument ${instId}` : 'Full Portfolio';
162
+
151
163
  if (isPrivate) {
152
- logger.log('TRACE', `[handleUpdate/${userId}] Skipping remaining instruments (User Private).`);
164
+ logger.log('TRACE', `[handleUpdate/${userId}] Skipping ${scopeName} (User Private).`);
153
165
  break;
154
166
  }
155
167
 
156
168
  const portfolioHeader = await headerManager.selectHeader();
157
- if (!portfolioHeader) { logger.log('ERROR', `[handleUpdate/${userId}] Could not select portfolio header for instId ${instId}. Skipping this instrument.`); continue; }
169
+ if (!portfolioHeader) { logger.log('ERROR', `[handleUpdate/${userId}] Could not select portfolio header for ${scopeName}. Skipping.`); continue; }
158
170
 
159
171
  const portfolioUrl = userType === 'speculator' ? `${config.ETORO_API_POSITIONS_URL}?cid=${userId}&InstrumentID=${instId}` : `${config.ETORO_API_PORTFOLIO_URL}?cid=${userId}`;
160
172
  const options = { headers: portfolioHeader.header };
@@ -165,7 +177,7 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
165
177
  // --- PROXY ATTEMPT ---
166
178
  if (shouldTryProxy()) {
167
179
  try {
168
- logger.log('TRACE', `[handleUpdate/${userId}] Attempting portfolio fetch via AppScript proxy...`);
180
+ logger.log('TRACE', `[handleUpdate/${userId}] Attempting fetch for ${scopeName} via AppScript proxy...`);
169
181
  response = await proxyManager.fetch(portfolioUrl, options);
170
182
  if (!response.ok) throw new Error(`AppScript proxy failed with status ${response.status}`);
171
183
  wasPortfolioSuccess = true;
@@ -174,7 +186,7 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
174
186
 
175
187
  } catch (proxyError) {
176
188
  recordProxyOutcome(false);
177
- logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch via Proxy FAILED. Error: ${proxyError.message}. Failures: ${_consecutiveProxyFailures}/${MAX_PROXY_FAILURES}.`, { error: proxyError.message, source: 'AppScript' });
189
+ logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch for ${scopeName} via Proxy FAILED. Error: ${proxyError.message}. Failures: ${_consecutiveProxyFailures}/${MAX_PROXY_FAILURES}.`, { error: proxyError.message, source: 'AppScript' });
178
190
  }
179
191
  }
180
192
 
@@ -184,9 +196,9 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
184
196
  response = await fetch(portfolioUrl, options);
185
197
  if (!response.ok) { const errorText = await response.text(); throw new Error(`Direct fetch failed with status ${response.status}. Response: ${errorText.substring(0, 200)}`); }
186
198
  wasPortfolioSuccess = true;
187
- logger.log('TRACE', `[handleUpdate/${userId}] Portfolio fetch direct success.`);
199
+ logger.log('TRACE', `[handleUpdate/${userId}] Portfolio fetch for ${scopeName} direct success.`);
188
200
  } catch (fallbackError) {
189
- logger.log('ERROR', `[handleUpdate/${userId}] Portfolio fetch direct fallback FAILED.`, { error: fallbackError.message, source: 'eToro/Network' });
201
+ logger.log('ERROR', `[handleUpdate/${userId}] Portfolio fetch for ${scopeName} direct fallback FAILED.`, { error: fallbackError.message, source: 'eToro/Network' });
190
202
  wasPortfolioSuccess = false;
191
203
  }
192
204
  }
@@ -200,14 +212,16 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
200
212
  const portfolioJson = JSON.parse(body);
201
213
  capturedPortfolio = portfolioJson; // Capture for detection
202
214
  await batchManager.addToPortfolioBatch(userId, portfolioBlockId, today, portfolioJson, userType, instId);
203
- logger.log('TRACE', `[handleUpdate/${userId}] Portfolio processed successfully.`);
215
+ logger.log('TRACE', `[handleUpdate/${userId}] Portfolio for ${scopeName} processed successfully.`);
204
216
 
205
217
  } catch (parseError) {
206
218
  wasPortfolioSuccess = false;
207
- logger.log('ERROR', `[handleUpdate/${userId}] FAILED TO PARSE JSON RESPONSE.`, { url: portfolioUrl, parseErrorMessage: parseError.message });
219
+ hasPortfolioErrors = true; // Mark error state
220
+ logger.log('ERROR', `[handleUpdate/${userId}] FAILED TO PARSE JSON RESPONSE for ${scopeName}.`, { url: portfolioUrl, parseErrorMessage: parseError.message });
208
221
  }
209
222
  } else {
210
- logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch failed for instId ${instId}.`);
223
+ hasPortfolioErrors = true; // Mark error state
224
+ logger.log('WARN', `[handleUpdate/${userId}] Portfolio fetch FAILED for ${scopeName}.`);
211
225
  }
212
226
 
213
227
  if (proxyUsedForPortfolio) { headerManager.updatePerformance(portfolioHeader.id, wasPortfolioSuccess); }
@@ -254,14 +268,22 @@ async function handleUpdate(task, taskId, { logger, headerManager, proxyManager,
254
268
  return;
255
269
  }
256
270
 
257
- // If not private, update all timestamps
271
+ // If not private AND no critical errors, update timestamps
272
+ // (We update timestamps even on partial failures for speculators to avoid infinite retry loops immediately,
273
+ // relying on the next scheduled run, but for Normal users, a failure usually means we should retry later.
274
+ // Current logic: Update timestamp to prevent immediate re-queueing.)
258
275
  for (const instrumentId of instrumentsToProcess) {
259
276
  await batchManager.updateUserTimestamp(userId, userType, instrumentId);
260
277
  }
261
278
 
262
279
  if (userType === 'speculator') { await batchManager.addSpeculatorTimestampFix(userId, String(Math.floor(userId/1e6)*1e6)); }
263
280
 
264
- logger.log('TRACE', `[handleUpdate/${userId}] Update task finished successfully.`);
281
+ // FIX 3: Honest Final Log
282
+ if (hasPortfolioErrors) {
283
+ logger.log('WARN', `[handleUpdate/${userId}] Update task finished with ERRORS. See logs above.`);
284
+ } else {
285
+ logger.log('TRACE', `[handleUpdate/${userId}] Update task finished successfully.`);
286
+ }
265
287
  }
266
288
 
267
289
  module.exports = { handleUpdate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.209",
3
+ "version": "1.0.211",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [