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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
|