bulltrackers-module 1.0.18 → 1.0.19
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.
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Core logic for fetching eToro instrument insights.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fetches insights data using header and proxy managers and stores it in Firestore.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} firestore - An initialized Firestore client instance.
|
|
11
|
+
* @param {object} logger - A logger instance.
|
|
12
|
+
* @param {object} headerManager - An initialized IntelligentHeaderManager instance.
|
|
13
|
+
* @param {object} proxyManager - An initialized IntelligentProxyManager instance.
|
|
14
|
+
* @param {object} config - Configuration object.
|
|
15
|
+
* @param {string} config.etoroInsightsUrl - The URL for the eToro insights API.
|
|
16
|
+
* @param {string} config.insightsCollectionName - Firestore collection name for results.
|
|
17
|
+
* @returns {Promise<{success: boolean, message: string, instrumentCount?: number}>}
|
|
18
|
+
*/
|
|
19
|
+
exports.fetchAndStoreInsights = async (firestore, logger, headerManager, proxyManager, config) => {
|
|
20
|
+
logger.log('INFO', '[FetchInsightsHelpers] Starting eToro insights data fetch...');
|
|
21
|
+
let selectedHeader = null;
|
|
22
|
+
let wasSuccessful = false;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Validate config
|
|
26
|
+
if (!config.etoroInsightsUrl || !config.insightsCollectionName) {
|
|
27
|
+
throw new Error("Missing required configuration: etoroInsightsUrl or insightsCollectionName.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
selectedHeader = await headerManager.selectHeader();
|
|
31
|
+
if (!selectedHeader) {
|
|
32
|
+
throw new Error("Could not select a header for the request.");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logger.log('INFO', `[FetchInsightsHelpers] Using header ID: ${selectedHeader.id}`, {
|
|
36
|
+
userAgent: selectedHeader.header['User-Agent'] // Access header data correctly
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Make the API request via the proxy manager
|
|
40
|
+
// Pass only the necessary parts of the selected header object
|
|
41
|
+
const fetchOptions = {
|
|
42
|
+
headers: selectedHeader.header, // Pass the actual header object
|
|
43
|
+
timeout: 30000 // 30 second timeout - Note: Proxy manager might have its own timeout logic
|
|
44
|
+
};
|
|
45
|
+
const { response } = await proxyManager.fetch(config.etoroInsightsUrl, fetchOptions);
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const errorText = await response.text();
|
|
49
|
+
// No need to update header performance here, proxy manager handles failures implicitly if needed
|
|
50
|
+
throw new Error(`API request failed via proxy with status ${response.status}: ${errorText}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// If fetch succeeded via proxy, mark header as successful
|
|
54
|
+
wasSuccessful = true; // Mark success for header update
|
|
55
|
+
|
|
56
|
+
const insightsData = await response.json();
|
|
57
|
+
|
|
58
|
+
if (!Array.isArray(insightsData) || insightsData.length === 0) {
|
|
59
|
+
throw new Error('API returned empty or invalid data.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Store in Firestore
|
|
63
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
64
|
+
const docRef = firestore.collection(config.insightsCollectionName).doc(today);
|
|
65
|
+
|
|
66
|
+
const firestorePayload = {
|
|
67
|
+
fetchedAt: FieldValue.serverTimestamp(),
|
|
68
|
+
instrumentCount: insightsData.length,
|
|
69
|
+
insights: insightsData
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
await docRef.set(firestorePayload);
|
|
73
|
+
|
|
74
|
+
const successMsg = `Successfully fetched and stored ${insightsData.length} instrument insights for ${today}.`;
|
|
75
|
+
logger.log('SUCCESS', `[FetchInsightsHelpers] ${successMsg}`, {
|
|
76
|
+
documentId: today,
|
|
77
|
+
instrumentCount: insightsData.length
|
|
78
|
+
});
|
|
79
|
+
return { success: true, message: successMsg, instrumentCount: insightsData.length };
|
|
80
|
+
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logger.log('ERROR', '[FetchInsightsHelpers] Error fetching eToro insights', {
|
|
83
|
+
errorMessage: error.message,
|
|
84
|
+
errorStack: error.stack,
|
|
85
|
+
headerId: selectedHeader ? selectedHeader.id : 'not-selected'
|
|
86
|
+
});
|
|
87
|
+
// Let the calling function handle header update on failure
|
|
88
|
+
throw error; // Re-throw the error to be caught by the handler
|
|
89
|
+
} finally {
|
|
90
|
+
// Update header performance based on the success *of the target API call*
|
|
91
|
+
if (selectedHeader) {
|
|
92
|
+
await headerManager.updatePerformance(selectedHeader.id, wasSuccessful);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Exports the FetchInsights handler creator.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Firestore } = require('@google-cloud/firestore');
|
|
6
|
+
const { logger } = require("sharedsetup")(__filename);
|
|
7
|
+
const { IntelligentHeaderManager, IntelligentProxyManager } = require('../core/utils');
|
|
8
|
+
const { fetchAndStoreInsights } = require('./helpers/handler_helpers');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates the Cloud Function handler for fetching eToro insights.
|
|
12
|
+
* @param {object} config - The configuration object loaded from the calling function's context.
|
|
13
|
+
* @returns {Function} The async Cloud Function handler (Express middleware format).
|
|
14
|
+
*/
|
|
15
|
+
function createFetchInsightsHandler(config) {
|
|
16
|
+
// Initialize clients once when the function instance starts
|
|
17
|
+
const firestore = new Firestore();
|
|
18
|
+
const headerManager = new IntelligentHeaderManager(firestore, logger, config); // Pass dependencies
|
|
19
|
+
const proxyManager = new IntelligentProxyManager(firestore, logger, config); // Pass dependencies
|
|
20
|
+
|
|
21
|
+
return async (req, res) => {
|
|
22
|
+
logger.log('INFO', '🚀 Fetch eToro Insights triggered via module...');
|
|
23
|
+
try {
|
|
24
|
+
// Delegate all logic to the helper
|
|
25
|
+
const result = await fetchAndStoreInsights(firestore, logger, headerManager, proxyManager, config);
|
|
26
|
+
|
|
27
|
+
// Send success response
|
|
28
|
+
res.status(200).send(result.message);
|
|
29
|
+
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.log('ERROR', 'FATAL Error in Fetch eToro Insights (Module)', { errorMessage: error.message, errorStack: error.stack });
|
|
32
|
+
// Header performance update on failure is handled within the helper's finally block if header was selected
|
|
33
|
+
|
|
34
|
+
// Ensure header performance flush happens even on fatal error before responding
|
|
35
|
+
try {
|
|
36
|
+
await headerManager.flushPerformanceUpdates();
|
|
37
|
+
} catch (flushError) {
|
|
38
|
+
logger.log('ERROR', 'Error flushing header performance during error handling', { flushError: flushError.message });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Send error response
|
|
42
|
+
res.status(500).send(`An internal error occurred: ${error.message}`);
|
|
43
|
+
} finally {
|
|
44
|
+
// Always attempt to flush performance updates at the end of the function execution
|
|
45
|
+
try {
|
|
46
|
+
await headerManager.flushPerformanceUpdates();
|
|
47
|
+
} catch (flushError) {
|
|
48
|
+
logger.log('ERROR', 'Error flushing header performance in final finally block', { flushError: flushError.message });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
createFetchInsightsHandler,
|
|
56
|
+
helpers: { fetchAndStoreInsights } // Export helpers if needed directly
|
|
57
|
+
};
|
package/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const GenericAPI = require('./functions/generic-api'); // <-- ADD THIS
|
|
|
12
12
|
const Dispatcher = require('./functions/dispatcher'); // <-- ADD THIS
|
|
13
13
|
const InvalidSpeculatorHandler = require('./functions/invalid-speculator-handler'); // <-- ADD THIS
|
|
14
14
|
const SpeculatorCleanupOrchestrator = require('./functions/speculator-cleanup-orchestrator'); // <-- ADD THIS
|
|
15
|
+
const FetchInsights = require('./functions/fetch-insights'); // <-- ADD THIS
|
|
15
16
|
|
|
16
17
|
module.exports = {
|
|
17
18
|
core,
|
|
@@ -21,5 +22,6 @@ module.exports = {
|
|
|
21
22
|
GenericAPI, // <-- AND ADD THIS
|
|
22
23
|
Dispatcher, // <-- AND ADD THIS
|
|
23
24
|
InvalidSpeculatorHandler, // <-- AND ADD THIS
|
|
24
|
-
SpeculatorCleanupOrchestrator // <-- AND ADD THIS
|
|
25
|
+
SpeculatorCleanupOrchestrator, // <-- AND ADD THIS
|
|
26
|
+
FetchInsights, // <-- AND ADD THIS
|
|
25
27
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bulltrackers-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "Helper Functions for Bulltrackers.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"functions/generic-api/",
|
|
13
13
|
"functions/dispatcher/",
|
|
14
14
|
"functions/invalid-speculator-handler/",
|
|
15
|
-
"functions/speculator-cleanup-orchestrator/"
|
|
15
|
+
"functions/speculator-cleanup-orchestrator/",
|
|
16
|
+
"functions/fetch-insights/"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"test": "echo \"Error: no test specified\" && exit 1"
|