bulltrackers-module 1.0.475 → 1.0.477
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/computation-system/helpers/on_demand_helpers.js +4 -1
- package/functions/computation-system/workflows/data_feeder_pipeline.yaml +8 -47
- package/functions/fetch-popular-investors/helpers/fetch_helpers.js +31 -0
- package/functions/generic-api/user-api/helpers/on_demand_fetch_helpers.js +4 -2
- package/functions/generic-api/user-api/helpers/user_sync_helpers.js +4 -2
- package/functions/generic-api/user-api/helpers/verification_helpers.js +8 -4
- package/functions/task-engine/handler_creator.js +6 -2
- package/package.json +1 -1
|
@@ -84,7 +84,10 @@ function resolveDependencyChain(computationName, manifest) {
|
|
|
84
84
|
*/
|
|
85
85
|
async function triggerComputationWithDependencies(targetComputation, date, dependencies, config, metadata = {}) {
|
|
86
86
|
const { pubsub, logger } = dependencies;
|
|
87
|
-
|
|
87
|
+
// Use on-demand topic for on-demand computation requests
|
|
88
|
+
const computationTopic = config.computationTopicOnDemand ||
|
|
89
|
+
(config.getComputationTopic && config.getComputationTopic(true)) ||
|
|
90
|
+
'computation-tasks-ondemand';
|
|
88
91
|
const topic = pubsub.topic(computationTopic);
|
|
89
92
|
const crypto = require('crypto');
|
|
90
93
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
# Data Feeder Pipeline (V3.
|
|
1
|
+
# Data Feeder Pipeline (V3.2 - Automatic Root Data Indexing)
|
|
2
2
|
# Starts at 22:00 UTC via Cloud Scheduler.
|
|
3
|
-
#
|
|
3
|
+
# UPDATED: Removed intermediate root data indexer calls - each fetcher now automatically triggers indexing.
|
|
4
|
+
# Only the global verification run at midnight remains.
|
|
4
5
|
|
|
5
6
|
main:
|
|
6
7
|
params: [input]
|
|
@@ -45,17 +46,7 @@ main:
|
|
|
45
46
|
- wait_10_after_price:
|
|
46
47
|
call: sys.sleep
|
|
47
48
|
args: { seconds: 600 } # 10 Minutes
|
|
48
|
-
|
|
49
|
-
# FIX 1: Split assign and call
|
|
50
|
-
- prepare_index_price:
|
|
51
|
-
assign:
|
|
52
|
-
- today: '${text.split(time.format(sys.now()), "T")[0]}'
|
|
53
|
-
- index_today_after_price:
|
|
54
|
-
call: http.post
|
|
55
|
-
args:
|
|
56
|
-
url: '${"https://" + location + "-" + project + ".cloudfunctions.net/root-data-indexer"}'
|
|
57
|
-
body: { targetDate: '${today}' }
|
|
58
|
-
auth: { type: OIDC }
|
|
49
|
+
# NOTE: Price fetcher now automatically triggers root data indexer after completion
|
|
59
50
|
|
|
60
51
|
- wait_10_before_insights:
|
|
61
52
|
call: sys.sleep
|
|
@@ -78,17 +69,7 @@ main:
|
|
|
78
69
|
- wait_10_after_insights:
|
|
79
70
|
call: sys.sleep
|
|
80
71
|
args: { seconds: 600 }
|
|
81
|
-
|
|
82
|
-
# FIX 2: Split assign and call
|
|
83
|
-
- prepare_index_insights:
|
|
84
|
-
assign:
|
|
85
|
-
- today: '${text.split(time.format(sys.now()), "T")[0]}'
|
|
86
|
-
- index_today_after_insights:
|
|
87
|
-
call: http.post
|
|
88
|
-
args:
|
|
89
|
-
url: '${"https://" + location + "-" + project + ".cloudfunctions.net/root-data-indexer"}'
|
|
90
|
-
body: { targetDate: '${today}' }
|
|
91
|
-
auth: { type: OIDC }
|
|
72
|
+
# NOTE: Insights fetcher now automatically triggers root data indexer after completion
|
|
92
73
|
|
|
93
74
|
# ==========================================
|
|
94
75
|
# PHASE 2: WAIT FOR MIDNIGHT
|
|
@@ -124,17 +105,7 @@ main:
|
|
|
124
105
|
- wait_10_after_rankings:
|
|
125
106
|
call: sys.sleep
|
|
126
107
|
args: { seconds: 600 }
|
|
127
|
-
|
|
128
|
-
# FIX 3: Split assign and call
|
|
129
|
-
- prepare_index_rankings:
|
|
130
|
-
assign:
|
|
131
|
-
- today: '${text.split(time.format(sys.now()), "T")[0]}'
|
|
132
|
-
- index_today_after_rankings:
|
|
133
|
-
call: http.post
|
|
134
|
-
args:
|
|
135
|
-
url: '${"https://" + location + "-" + project + ".cloudfunctions.net/root-data-indexer"}'
|
|
136
|
-
body: { targetDate: '${today}' }
|
|
137
|
-
auth: { type: OIDC }
|
|
108
|
+
# NOTE: Popular investor rankings fetcher now automatically triggers root data indexer after completion
|
|
138
109
|
|
|
139
110
|
- phase_0000_social:
|
|
140
111
|
try:
|
|
@@ -158,7 +129,7 @@ main:
|
|
|
158
129
|
call: http.post
|
|
159
130
|
args:
|
|
160
131
|
url: '${"https://" + location + "-" + project + ".cloudfunctions.net/root-data-indexer"}'
|
|
161
|
-
# No targetDate = Global
|
|
132
|
+
# No targetDate = Global verification run (all intermediate indexing is now automatic)
|
|
162
133
|
auth: { type: OIDC }
|
|
163
134
|
|
|
164
135
|
# ==========================================
|
|
@@ -194,17 +165,7 @@ main:
|
|
|
194
165
|
- wait_10_in_loop:
|
|
195
166
|
call: sys.sleep
|
|
196
167
|
args: { seconds: 600 }
|
|
197
|
-
|
|
198
|
-
# FIX 4: Split assign and call
|
|
199
|
-
- prepare_index_loop:
|
|
200
|
-
assign:
|
|
201
|
-
- today: '${text.split(time.format(sys.now()), "T")[0]}'
|
|
202
|
-
- index_today_in_loop:
|
|
203
|
-
call: http.post
|
|
204
|
-
args:
|
|
205
|
-
url: '${"https://" + location + "-" + project + ".cloudfunctions.net/root-data-indexer"}'
|
|
206
|
-
body: { targetDate: '${today}' }
|
|
207
|
-
auth: { type: OIDC }
|
|
168
|
+
# NOTE: Social tasks are handled by task engine, which automatically triggers root data indexer after batch completion
|
|
208
169
|
|
|
209
170
|
# FIX 5: Correct assign syntax (must be a list)
|
|
210
171
|
- increment_loop:
|
|
@@ -113,6 +113,37 @@ async function fetchAndStorePopularInvestors(config, dependencies) {
|
|
|
113
113
|
});
|
|
114
114
|
|
|
115
115
|
logger.log('SUCCESS', `[PopularInvestorFetch] Stored ${data.TotalRows} rankings into ${rankingsCollectionName}/${today}`);
|
|
116
|
+
|
|
117
|
+
// Update root data indexer for today's date after rankings data is stored
|
|
118
|
+
try {
|
|
119
|
+
const { runRootDataIndexer } = require('../../root-data-indexer/index');
|
|
120
|
+
// Access rootDataIndexer from config (passed from index.js) or use defaults
|
|
121
|
+
// Using bracket notation to avoid TypeScript errors
|
|
122
|
+
let rootDataIndexerConfig;
|
|
123
|
+
if (config && typeof config === 'object' && config['rootDataIndexer']) {
|
|
124
|
+
rootDataIndexerConfig = config['rootDataIndexer'];
|
|
125
|
+
} else {
|
|
126
|
+
rootDataIndexerConfig = {
|
|
127
|
+
availabilityCollection: 'system_root_data_index',
|
|
128
|
+
earliestDate: '2025-08-01',
|
|
129
|
+
collections: {
|
|
130
|
+
piRankings: rankingsCollectionName
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const indexerConfig = Object.assign({}, rootDataIndexerConfig, {
|
|
136
|
+
targetDate: today // Index only today's date for speed
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
logger.log('INFO', `[PopularInvestorFetch] Triggering root data indexer for date ${today} after rankings data storage...`);
|
|
140
|
+
await runRootDataIndexer(indexerConfig, dependencies);
|
|
141
|
+
logger.log('INFO', `[PopularInvestorFetch] Root data indexer completed for date ${today}`);
|
|
142
|
+
} catch (indexerError) {
|
|
143
|
+
logger.log('ERROR', `[PopularInvestorFetch] Failed to run root data indexer for ${today}`, indexerError);
|
|
144
|
+
// Continue - rankings data is stored, indexer failure is non-critical
|
|
145
|
+
}
|
|
146
|
+
|
|
116
147
|
return { success: true, count: data.TotalRows };
|
|
117
148
|
|
|
118
149
|
} catch (dbError) {
|
|
@@ -134,8 +134,10 @@ async function requestPiFetch(req, res, dependencies, config) {
|
|
|
134
134
|
updatedAt: FieldValue.serverTimestamp()
|
|
135
135
|
}, { merge: true });
|
|
136
136
|
|
|
137
|
-
// Publish to task engine
|
|
138
|
-
const topicName = config.taskEngine?.
|
|
137
|
+
// Publish to task engine - use on-demand topic for API requests
|
|
138
|
+
const topicName = config.taskEngine?.PUBSUB_TOPIC_USER_FETCH_ONDEMAND ||
|
|
139
|
+
config.pubsubTopicUserFetchOnDemand ||
|
|
140
|
+
'etoro-user-fetch-topic-ondemand';
|
|
139
141
|
const topic = pubsub.topic(topicName);
|
|
140
142
|
|
|
141
143
|
const message = {
|
|
@@ -148,8 +148,10 @@ async function requestUserSync(req, res, dependencies, config) {
|
|
|
148
148
|
updatedAt: FieldValue.serverTimestamp()
|
|
149
149
|
}, { merge: true });
|
|
150
150
|
|
|
151
|
-
// Publish to task engine
|
|
152
|
-
const topicName = config.taskEngine?.
|
|
151
|
+
// Publish to task engine - use on-demand topic for API requests
|
|
152
|
+
const topicName = config.taskEngine?.PUBSUB_TOPIC_USER_FETCH_ONDEMAND ||
|
|
153
|
+
config.pubsubTopicUserFetchOnDemand ||
|
|
154
|
+
'etoro-user-fetch-topic-ondemand';
|
|
153
155
|
const topic = pubsub.topic(topicName);
|
|
154
156
|
|
|
155
157
|
const message = {
|
|
@@ -57,8 +57,12 @@ async function finalizeVerification(req, res, dependencies, config) {
|
|
|
57
57
|
proxyConfig,
|
|
58
58
|
headerConfig,
|
|
59
59
|
pubsubTopicUserFetch,
|
|
60
|
+
pubsubTopicUserFetchOnDemand,
|
|
60
61
|
pubsubTopicSocialFetch
|
|
61
62
|
} = config;
|
|
63
|
+
|
|
64
|
+
// Use on-demand topic for user signup (API-triggered)
|
|
65
|
+
const taskEngineTopic = pubsubTopicUserFetchOnDemand || pubsubTopicUserFetch || 'etoro-user-fetch-topic-ondemand';
|
|
62
66
|
|
|
63
67
|
if (!username) return res.status(400).json({ error: "Missing username." });
|
|
64
68
|
|
|
@@ -187,8 +191,8 @@ async function finalizeVerification(req, res, dependencies, config) {
|
|
|
187
191
|
|
|
188
192
|
// Only trigger if user is public (has portfolio data)
|
|
189
193
|
if (!isOptOut) {
|
|
190
|
-
await pubsubUtils.publish(
|
|
191
|
-
logger.log('INFO', `[Verification] Triggered unified data fetch (portfolio + social) for ${username} (${realCID})`);
|
|
194
|
+
await pubsubUtils.publish(taskEngineTopic, unifiedTask);
|
|
195
|
+
logger.log('INFO', `[Verification] Triggered unified data fetch (portfolio + social) for ${username} (${realCID}) via on-demand topic`);
|
|
192
196
|
} else {
|
|
193
197
|
// For private users, still fetch social data but no portfolio
|
|
194
198
|
const socialOnlyTask = {
|
|
@@ -208,8 +212,8 @@ async function finalizeVerification(req, res, dependencies, config) {
|
|
|
208
212
|
requestedAt: new Date().toISOString()
|
|
209
213
|
}
|
|
210
214
|
};
|
|
211
|
-
await pubsubUtils.publish(
|
|
212
|
-
logger.log('INFO', `[Verification] Triggered social-only fetch for private user ${username} (${realCID})`);
|
|
215
|
+
await pubsubUtils.publish(taskEngineTopic, socialOnlyTask);
|
|
216
|
+
logger.log('INFO', `[Verification] Triggered social-only fetch for private user ${username} (${realCID}) via on-demand topic`);
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
return res.status(200).json({
|
|
@@ -13,15 +13,19 @@ const { handleSocialFetch } = require('./helpers/social_helpers');
|
|
|
13
13
|
const { executeTasks, prepareTaskBatches } = require('./utils/task_engine_utils');
|
|
14
14
|
|
|
15
15
|
async function handleRequest(message, context, configObj, dependencies) {
|
|
16
|
-
// Support both old format (single config) and new format (object with taskEngine, rootDataIndexer, and
|
|
16
|
+
// Support both old format (single config) and new format (object with taskEngine, rootDataIndexer, social, and computationSystem)
|
|
17
17
|
const config = configObj.taskEngine || configObj; // Backward compatibility
|
|
18
18
|
const rootDataIndexerConfig = configObj.rootDataIndexer;
|
|
19
19
|
const socialConfig = configObj.social;
|
|
20
|
+
const computationSystemConfig = configObj.computationSystem;
|
|
20
21
|
|
|
21
|
-
// Merge
|
|
22
|
+
// Merge configs into main config for easy access
|
|
22
23
|
if (socialConfig) {
|
|
23
24
|
config.social = socialConfig;
|
|
24
25
|
}
|
|
26
|
+
if (computationSystemConfig) {
|
|
27
|
+
config.computationSystem = computationSystemConfig;
|
|
28
|
+
}
|
|
25
29
|
const { logger, batchManager, db } = dependencies;
|
|
26
30
|
|
|
27
31
|
// [CRITICAL FIX] Max Age increased to 25m to match the larger dedup window.
|