bulltrackers-module 1.0.665 → 1.0.667

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.
@@ -130,10 +130,16 @@ async function getStableDateSession(config, dependencies, pass, dateLimitStr, fo
130
130
  // HANDLERS
131
131
  // =============================================================================
132
132
 
133
- // 2. NEW SNAPSHOT HANDLER
133
+ // 2. NEW SNAPSHOT HANDLER (Asynchronous with Callback)
134
134
  async function handleSnapshot(config, dependencies, reqBody) {
135
135
  const { logger } = dependencies;
136
136
  const targetDate = reqBody.date; // Optional: if provided, only process up to this date
137
+ const callbackUrl = reqBody.callback_url; // Callback URL from workflow
138
+
139
+ logger.log('INFO', `[Dispatcher] 📸 Snapshot request received. Date: ${targetDate || 'all'}, Callback URL: ${callbackUrl || 'NOT PROVIDED'}`);
140
+
141
+ // Use native fetch if available (Node 18+), otherwise fall back to node-fetch
142
+ const directFetch = typeof fetch !== 'undefined' ? fetch : require('node-fetch');
137
143
 
138
144
  try {
139
145
  // Get earliest available root data date
@@ -156,7 +162,39 @@ async function handleSnapshot(config, dependencies, reqBody) {
156
162
 
157
163
  if (dateStrings.length === 0) {
158
164
  logger.log('WARN', '[Dispatcher] No dates to process for snapshot');
159
- return { status: 'OK', processed: 0, skipped: 0 };
165
+ const finalResult = { status: 'OK', processed: 0, skipped: 0 };
166
+
167
+ // Send callback if provided
168
+ if (callbackUrl) {
169
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
170
+ try {
171
+ // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
172
+ const timeoutPromise = new Promise((_, reject) =>
173
+ setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
174
+ );
175
+
176
+ const fetchPromise = directFetch(callbackUrl, {
177
+ method: 'POST',
178
+ headers: { 'Content-Type': 'application/json' },
179
+ body: JSON.stringify(finalResult)
180
+ });
181
+
182
+ const response = await Promise.race([fetchPromise, timeoutPromise]);
183
+
184
+ if (!response.ok) {
185
+ const errorText = await response.text().catch(() => 'Unable to read response');
186
+ logger.log('ERROR', `[Dispatcher] Callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
187
+ } else {
188
+ logger.log('INFO', `[Dispatcher] ✅ Callback sent successfully (status: ${response.status})`);
189
+ }
190
+ } catch (err) {
191
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
192
+ }
193
+ } else {
194
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
195
+ }
196
+
197
+ return finalResult;
160
198
  }
161
199
 
162
200
  logger.log('INFO', `[Dispatcher] 📸 Processing snapshots for ${dateStrings.length} dates from ${dateStrings[0]} to ${dateStrings[dateStrings.length - 1]}`);
@@ -189,7 +227,7 @@ async function handleSnapshot(config, dependencies, reqBody) {
189
227
 
190
228
  logger.log('INFO', `[Dispatcher] 📸 Snapshot batch complete: ${successful} processed, ${skipped} skipped, ${failed} failed out of ${results.length} total`);
191
229
 
192
- return {
230
+ const finalResult = {
193
231
  status: failed === 0 ? 'OK' : 'PARTIAL',
194
232
  processed: successful,
195
233
  skipped: skipped,
@@ -197,10 +235,73 @@ async function handleSnapshot(config, dependencies, reqBody) {
197
235
  total: results.length,
198
236
  results: results
199
237
  };
238
+
239
+ // Send callback to workflow if provided
240
+ if (callbackUrl) {
241
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
242
+ try {
243
+ // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
244
+ const timeoutPromise = new Promise((_, reject) =>
245
+ setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
246
+ );
247
+
248
+ const fetchPromise = directFetch(callbackUrl, {
249
+ method: 'POST',
250
+ headers: { 'Content-Type': 'application/json' },
251
+ body: JSON.stringify(finalResult)
252
+ });
253
+
254
+ const response = await Promise.race([fetchPromise, timeoutPromise]);
255
+
256
+ if (!response.ok) {
257
+ const errorText = await response.text().catch(() => 'Unable to read response');
258
+ logger.log('ERROR', `[Dispatcher] Callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
259
+ } else {
260
+ logger.log('INFO', `[Dispatcher] ✅ Callback sent successfully (status: ${response.status})`);
261
+ }
262
+ } catch (err) {
263
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
264
+ }
265
+ } else {
266
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
267
+ }
268
+
269
+ return finalResult;
200
270
  } catch (e) {
201
271
  logger.log('ERROR', `[Dispatcher] Snapshot failed: ${e.message}`);
202
- // Return error object so workflow can see failure
203
- return { status: 'ERROR', error: e.message };
272
+ const errorResult = { status: 'ERROR', error: e.message };
273
+
274
+ // Notify workflow of failure too, otherwise it hangs!
275
+ if (callbackUrl) {
276
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow with error at: ${callbackUrl}`);
277
+ try {
278
+ // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
279
+ const timeoutPromise = new Promise((_, reject) =>
280
+ setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
281
+ );
282
+
283
+ const fetchPromise = directFetch(callbackUrl, {
284
+ method: 'POST',
285
+ headers: { 'Content-Type': 'application/json' },
286
+ body: JSON.stringify(errorResult)
287
+ });
288
+
289
+ const response = await Promise.race([fetchPromise, timeoutPromise]);
290
+
291
+ if (!response.ok) {
292
+ const errorText = await response.text().catch(() => 'Unable to read response');
293
+ logger.log('ERROR', `[Dispatcher] Error callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
294
+ } else {
295
+ logger.log('INFO', `[Dispatcher] ✅ Error callback sent successfully (status: ${response.status})`);
296
+ }
297
+ } catch (err) {
298
+ logger.log('ERROR', `[Dispatcher] Error callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
299
+ }
300
+ } else {
301
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified of error');
302
+ }
303
+
304
+ return errorResult;
204
305
  }
205
306
  }
206
307
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.665",
3
+ "version": "1.0.667",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [