bulltrackers-module 1.0.17 → 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.
- package/functions/fetch-insights/helpers/handler_helpers.js +95 -0
- package/functions/fetch-insights/index.js +57 -0
- package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +179 -0
- package/functions/speculator-cleanup-orchestrator/index.js +43 -0
- package/index.js +4 -0
- package/package.json +4 -2
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Core logic for the speculator cleanup orchestrator.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cleans up stale entries from the pending speculators collection.
|
|
9
|
+
* @param {object} firestore - An initialized Firestore client.
|
|
10
|
+
* @param {object} logger - A logger instance.
|
|
11
|
+
* @param {object} config - Configuration object.
|
|
12
|
+
* @param {string} config.pendingSpeculatorsCollectionName - Name of the pending collection.
|
|
13
|
+
* @param {number} config.pendingGracePeriodHours - Hours before a pending entry is considered stale.
|
|
14
|
+
* @returns {Promise<{ batch: object, count: number }>} - The batch object with updates and the count of removed users.
|
|
15
|
+
*/
|
|
16
|
+
async function cleanupPendingSpeculators(firestore, logger, config) {
|
|
17
|
+
logger.log('INFO', '[CleanupHelpers] Starting pending speculator cleanup...');
|
|
18
|
+
const batch = firestore.batch();
|
|
19
|
+
let stalePendingUsersRemoved = 0;
|
|
20
|
+
const pendingCollectionRef = firestore.collection(config.pendingSpeculatorsCollectionName);
|
|
21
|
+
const staleThreshold = new Date();
|
|
22
|
+
staleThreshold.setHours(staleThreshold.getHours() - config.pendingGracePeriodHours);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const pendingSnapshot = await pendingCollectionRef.get();
|
|
26
|
+
if (pendingSnapshot.empty) {
|
|
27
|
+
logger.log('INFO', '[CleanupHelpers] Pending speculators collection is empty.');
|
|
28
|
+
return { batch, count: 0 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const doc of pendingSnapshot.docs) {
|
|
32
|
+
const pendingData = doc.data().users || {};
|
|
33
|
+
const updates = {};
|
|
34
|
+
let updatesInDoc = 0;
|
|
35
|
+
|
|
36
|
+
for (const userId in pendingData) {
|
|
37
|
+
// Ensure timestamp exists and is a Firestore Timestamp before converting
|
|
38
|
+
const timestamp = pendingData[userId]?.toDate ? pendingData[userId].toDate() : null;
|
|
39
|
+
if (timestamp && timestamp < staleThreshold) {
|
|
40
|
+
updates[`users.${userId}`] = FieldValue.delete();
|
|
41
|
+
stalePendingUsersRemoved++;
|
|
42
|
+
updatesInDoc++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (updatesInDoc > 0) {
|
|
47
|
+
logger.log('TRACE', `[CleanupHelpers] Marking ${updatesInDoc} users for removal from pending doc ${doc.id}`);
|
|
48
|
+
batch.update(doc.ref, updates);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
logger.log('INFO', `[CleanupHelpers] Marked ${stalePendingUsersRemoved} total stale pending users for removal.`);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.log('ERROR', '[CleanupHelpers] Error cleaning pending speculators', { errorMessage: error.message });
|
|
54
|
+
throw error; // Re-throw to be caught by the main handler
|
|
55
|
+
}
|
|
56
|
+
return { batch, count: stalePendingUsersRemoved };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Cleans up stale speculators from the main blocks based on inactivity grace period.
|
|
61
|
+
* @param {object} firestore - An initialized Firestore client.
|
|
62
|
+
* @param {object} logger - A logger instance.
|
|
63
|
+
* @param {object} config - Configuration object.
|
|
64
|
+
* @param {string} config.speculatorBlocksCollectionName - Name of the speculator blocks collection.
|
|
65
|
+
* @param {string} config.speculatorBlockCountsDocPath - Path to the block counts document.
|
|
66
|
+
* @param {number} config.activityGracePeriodDays - Days of inactivity before removal.
|
|
67
|
+
* @param {object} batch - The Firestore batch object to add updates to.
|
|
68
|
+
* @returns {Promise<{ batch: object, count: number }>} - The batch object with updates and the count of removed users.
|
|
69
|
+
*/
|
|
70
|
+
async function cleanupStaleSpeculators(firestore, logger, config, batch) {
|
|
71
|
+
logger.log('INFO', '[CleanupHelpers] Starting stale speculator cleanup from blocks...');
|
|
72
|
+
let totalUsersRemoved = 0;
|
|
73
|
+
const blocksCollectionRef = firestore.collection(config.speculatorBlocksCollectionName);
|
|
74
|
+
const gracePeriodDate = new Date();
|
|
75
|
+
gracePeriodDate.setDate(gracePeriodDate.getDate() - config.activityGracePeriodDays);
|
|
76
|
+
const blockCountsUpdate = {}; // To track decrements per instrument/block
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const blocksSnapshot = await blocksCollectionRef.get();
|
|
80
|
+
if (blocksSnapshot.empty) {
|
|
81
|
+
logger.log('INFO', '[CleanupHelpers] Speculator blocks collection is empty.');
|
|
82
|
+
return { batch, count: 0 };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const doc of blocksSnapshot.docs) {
|
|
86
|
+
const blockId = doc.id; // e.g., "19000000"
|
|
87
|
+
const blockData = doc.data();
|
|
88
|
+
// Firestore map keys cannot contain '.', so the keys are likely 'users.123456'
|
|
89
|
+
const users = blockData.users || {};
|
|
90
|
+
let usersRemovedFromBlock = 0;
|
|
91
|
+
const updates = {}; // Updates specific to this block document
|
|
92
|
+
|
|
93
|
+
for (const userKey in users) { // userKey is like "users.123456"
|
|
94
|
+
// Extract CID correctly
|
|
95
|
+
const userId = userKey.split('.')[1];
|
|
96
|
+
if (!userId) continue; // Skip malformed keys
|
|
97
|
+
|
|
98
|
+
const userData = users[userKey];
|
|
99
|
+
// Ensure timestamp exists and is a Firestore Timestamp before converting
|
|
100
|
+
const lastHeldTimestamp = userData.lastHeldSpeculatorAsset?.toDate ? userData.lastHeldSpeculatorAsset.toDate() : null;
|
|
101
|
+
|
|
102
|
+
// Check if the user is stale based on the grace period
|
|
103
|
+
if (lastHeldTimestamp && lastHeldTimestamp < gracePeriodDate) {
|
|
104
|
+
updates[userKey] = FieldValue.delete(); // Delete the specific user field
|
|
105
|
+
usersRemovedFromBlock++;
|
|
106
|
+
|
|
107
|
+
// Decrement block counts for each instrument this user held in this block
|
|
108
|
+
if (userData.instruments && Array.isArray(userData.instruments)) {
|
|
109
|
+
userData.instruments.forEach(instrumentId => {
|
|
110
|
+
const instrumentBlockKey = `${instrumentId}_${blockId}`;
|
|
111
|
+
if (!blockCountsUpdate[instrumentBlockKey]) {
|
|
112
|
+
blockCountsUpdate[instrumentBlockKey] = 0;
|
|
113
|
+
}
|
|
114
|
+
blockCountsUpdate[instrumentBlockKey]--; // Decrement count
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If users were marked for removal in this block, add the update to the batch
|
|
121
|
+
if (usersRemovedFromBlock > 0) {
|
|
122
|
+
logger.log('TRACE', `[CleanupHelpers] Marking ${usersRemovedFromBlock} users for removal from block ${blockId}.`);
|
|
123
|
+
batch.update(doc.ref, updates);
|
|
124
|
+
totalUsersRemoved += usersRemovedFromBlock;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// After iterating all blocks, if any users were removed, add the block count decrements to the batch
|
|
129
|
+
if (totalUsersRemoved > 0 && Object.keys(blockCountsUpdate).length > 0) {
|
|
130
|
+
const countsRef = firestore.doc(config.speculatorBlockCountsDocPath);
|
|
131
|
+
const finalCountUpdates = {};
|
|
132
|
+
for (const key in blockCountsUpdate) {
|
|
133
|
+
// Use dot notation for nested map fields
|
|
134
|
+
finalCountUpdates[`counts.${key}`] = FieldValue.increment(blockCountsUpdate[key]);
|
|
135
|
+
}
|
|
136
|
+
logger.log('TRACE', '[CleanupHelpers] Staging block count decrements.', { updates: finalCountUpdates });
|
|
137
|
+
batch.set(countsRef, finalCountUpdates, { merge: true }); // Use set with merge: true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
logger.log('INFO', `[CleanupHelpers] Marked ${totalUsersRemoved} total stale speculators for removal from blocks.`);
|
|
141
|
+
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.log('ERROR', '[CleanupHelpers] Error cleaning stale speculators', { errorMessage: error.message });
|
|
144
|
+
throw error; // Re-throw
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { batch, count: totalUsersRemoved };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Orchestrates the cleanup process.
|
|
152
|
+
* @param {object} firestore - An initialized Firestore client.
|
|
153
|
+
* @param {object} logger - A logger instance.
|
|
154
|
+
* @param {object} config - Configuration object.
|
|
155
|
+
*/
|
|
156
|
+
exports.runCleanup = async (firestore, logger, config) => {
|
|
157
|
+
logger.log('INFO', '[CleanupHelpers] Running cleanup orchestrator...');
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Start cleanup for pending users
|
|
161
|
+
const { batch: batchAfterPending, count: pendingRemoved } = await cleanupPendingSpeculators(firestore, logger, config);
|
|
162
|
+
|
|
163
|
+
// Continue with the same batch for stale speculators
|
|
164
|
+
const { batch: finalBatch, count: staleRemoved } = await cleanupStaleSpeculators(firestore, logger, config, batchAfterPending);
|
|
165
|
+
|
|
166
|
+
// Commit the batch if any changes were made
|
|
167
|
+
if (pendingRemoved > 0 || staleRemoved > 0) {
|
|
168
|
+
await finalBatch.commit();
|
|
169
|
+
logger.log('SUCCESS', `[CleanupHelpers] Cleanup commit successful. Removed ${pendingRemoved} pending, ${staleRemoved} stale speculators.`);
|
|
170
|
+
return { pendingRemoved, staleRemoved };
|
|
171
|
+
} else {
|
|
172
|
+
logger.log('SUCCESS', '[CleanupHelpers] No stale users found in pending or blocks.');
|
|
173
|
+
return { pendingRemoved: 0, staleRemoved: 0 };
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
logger.log('ERROR', '[CleanupHelpers] FATAL error during cleanup orchestration', { errorMessage: error.message, errorStack: error.stack });
|
|
177
|
+
throw error; // Re-throw for the main handler
|
|
178
|
+
}
|
|
179
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Exports the SpeculatorCleanupOrchestrator handler creator.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Firestore } = require('@google-cloud/firestore');
|
|
6
|
+
const { logger } = require("sharedsetup")(__filename);
|
|
7
|
+
const { runCleanup } = require('./helpers/cleanup_helpers');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates the Cloud Function handler for speculator cleanup.
|
|
11
|
+
* @param {object} config - The configuration object loaded from the calling function's context.
|
|
12
|
+
* @returns {Function} The async Cloud Function handler (Express middleware format).
|
|
13
|
+
*/
|
|
14
|
+
function createSpeculatorCleanupHandler(config) {
|
|
15
|
+
// Initialize Firestore client once when the function instance starts
|
|
16
|
+
const firestore = new Firestore();
|
|
17
|
+
|
|
18
|
+
return async (req, res) => {
|
|
19
|
+
logger.log('INFO', '🚀 Speculator Cleanup Orchestrator triggered via module...');
|
|
20
|
+
try {
|
|
21
|
+
// Validate essential config
|
|
22
|
+
if (!config || !config.speculatorBlocksCollectionName || !config.speculatorBlockCountsDocPath || !config.pendingSpeculatorsCollectionName) {
|
|
23
|
+
throw new Error("Speculator Cleanup Orchestrator received invalid configuration.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Delegate all logic to the helper
|
|
27
|
+
const { pendingRemoved, staleRemoved } = await runCleanup(firestore, logger, config);
|
|
28
|
+
|
|
29
|
+
// Send success response
|
|
30
|
+
res.status(200).send(`Cleanup complete via module. Removed ${staleRemoved} stale speculators, ${pendingRemoved} stale pending users.`);
|
|
31
|
+
|
|
32
|
+
} catch (error) {
|
|
33
|
+
logger.log('ERROR', 'FATAL Error in Speculator Cleanup Orchestrator (Module)', { errorMessage: error.message, errorStack: error.stack });
|
|
34
|
+
// Send error response
|
|
35
|
+
res.status(500).send("An internal cleanup error occurred (via module).");
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
createSpeculatorCleanupHandler,
|
|
42
|
+
helpers: { runCleanup } // Export helpers if needed directly
|
|
43
|
+
};
|
package/index.js
CHANGED
|
@@ -11,6 +11,8 @@ const ComputationSystem = require('./functions/computation-system');
|
|
|
11
11
|
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
|
+
const SpeculatorCleanupOrchestrator = require('./functions/speculator-cleanup-orchestrator'); // <-- ADD THIS
|
|
15
|
+
const FetchInsights = require('./functions/fetch-insights'); // <-- ADD THIS
|
|
14
16
|
|
|
15
17
|
module.exports = {
|
|
16
18
|
core,
|
|
@@ -20,4 +22,6 @@ module.exports = {
|
|
|
20
22
|
GenericAPI, // <-- AND ADD THIS
|
|
21
23
|
Dispatcher, // <-- AND ADD THIS
|
|
22
24
|
InvalidSpeculatorHandler, // <-- AND ADD THIS
|
|
25
|
+
SpeculatorCleanupOrchestrator, // <-- AND ADD THIS
|
|
26
|
+
FetchInsights, // <-- AND ADD THIS
|
|
23
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": [
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"functions/computation-system/",
|
|
12
12
|
"functions/generic-api/",
|
|
13
13
|
"functions/dispatcher/",
|
|
14
|
-
"functions/invalid-speculator-handler/"
|
|
14
|
+
"functions/invalid-speculator-handler/",
|
|
15
|
+
"functions/speculator-cleanup-orchestrator/",
|
|
16
|
+
"functions/fetch-insights/"
|
|
15
17
|
],
|
|
16
18
|
"scripts": {
|
|
17
19
|
"test": "echo \"Error: no test specified\" && exit 1"
|