bulltrackers-module 1.0.105 → 1.0.107

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.
Files changed (33) hide show
  1. package/README.MD +222 -222
  2. package/functions/appscript-api/helpers/errors.js +19 -19
  3. package/functions/appscript-api/index.js +58 -58
  4. package/functions/computation-system/helpers/orchestration_helpers.js +667 -113
  5. package/functions/computation-system/utils/data_loader.js +191 -191
  6. package/functions/computation-system/utils/utils.js +149 -254
  7. package/functions/core/utils/firestore_utils.js +433 -433
  8. package/functions/core/utils/pubsub_utils.js +53 -53
  9. package/functions/dispatcher/helpers/dispatch_helpers.js +47 -47
  10. package/functions/dispatcher/index.js +52 -52
  11. package/functions/etoro-price-fetcher/helpers/handler_helpers.js +124 -124
  12. package/functions/fetch-insights/helpers/handler_helpers.js +91 -91
  13. package/functions/generic-api/helpers/api_helpers.js +379 -379
  14. package/functions/generic-api/index.js +150 -150
  15. package/functions/invalid-speculator-handler/helpers/handler_helpers.js +75 -75
  16. package/functions/orchestrator/helpers/discovery_helpers.js +226 -226
  17. package/functions/orchestrator/helpers/update_helpers.js +92 -92
  18. package/functions/orchestrator/index.js +147 -147
  19. package/functions/price-backfill/helpers/handler_helpers.js +116 -123
  20. package/functions/social-orchestrator/helpers/orchestrator_helpers.js +61 -61
  21. package/functions/social-task-handler/helpers/handler_helpers.js +288 -288
  22. package/functions/task-engine/handler_creator.js +78 -78
  23. package/functions/task-engine/helpers/discover_helpers.js +125 -125
  24. package/functions/task-engine/helpers/update_helpers.js +118 -118
  25. package/functions/task-engine/helpers/verify_helpers.js +162 -162
  26. package/functions/task-engine/utils/firestore_batch_manager.js +258 -258
  27. package/index.js +105 -113
  28. package/package.json +45 -45
  29. package/functions/computation-system/computation_dependencies.json +0 -120
  30. package/functions/computation-system/helpers/worker_helpers.js +0 -340
  31. package/functions/computation-system/utils/computation_state_manager.js +0 -178
  32. package/functions/computation-system/utils/dependency_graph.js +0 -191
  33. package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +0 -160
@@ -1,226 +1,226 @@
1
- /**
2
- * @fileoverview Orchestrator's discovery sub-pipes.
3
- * REFACTORED: All functions are now stateless and receive dependencies.
4
- */
5
-
6
- /**
7
- * Sub-pipe: pipe.orchestrator.checkDiscoveryNeed
8
- * @param {string} userType - 'normal' or 'speculator'.
9
- * @param {object} config - The user type specific config (e.g., config.discoveryConfig.normal).
10
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
11
- * @returns {Promise<{needsDiscovery: boolean, blocksToFill: Array<object>}>}
12
- */
13
- async function checkDiscoveryNeed(userType, config, dependencies) {
14
- const { logger, firestoreUtils } = dependencies;
15
- logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
16
-
17
- const {
18
- targetUsersPerBlock,
19
- speculatorBlockCountsDocPath,
20
- normalBlockCountsDocPath,
21
- speculatorInstruments,
22
- allHighValueBlocks
23
- } = config;
24
-
25
- const isSpeculator = userType === 'speculator';
26
-
27
- // Pass dependencies and config to core util
28
- const blockCounts = await firestoreUtils.getBlockCapacities(
29
- dependencies,
30
- { speculatorBlockCountsDocPath, normalBlockCountsDocPath },
31
- userType
32
- );
33
-
34
- let underPopulatedBlocks = [];
35
- if (isSpeculator) {
36
- for (const instrument of speculatorInstruments) {
37
- for (const block of allHighValueBlocks) {
38
- const instrumentBlockKey = `${instrument}_${block.startId}`;
39
- if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {
40
- underPopulatedBlocks.push({ ...block, instrument });
41
- }
42
- }
43
- }
44
- } else {
45
- underPopulatedBlocks = allHighValueBlocks.filter(
46
- block => (blockCounts[block.startId] || 0) < targetUsersPerBlock
47
- );
48
- }
49
-
50
- const needsDiscovery = underPopulatedBlocks.length > 0;
51
- if (!needsDiscovery) {
52
- logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
53
- } else {
54
- logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);
55
- }
56
- return { needsDiscovery, blocksToFill: underPopulatedBlocks };
57
- }
58
-
59
- /**
60
- * Sub-pipe: pipe.orchestrator.getDiscoveryCandidates
61
- * @param {string} userType - 'normal' or 'speculator'.
62
- * @param {Array<object>} blocksToFill - From checkDiscoveryNeed.
63
- * @param {object} config - The user type specific config.
64
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
65
- * @returns {Promise<Set<string>>} Set of candidate CIDs.
66
- */
67
- async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
68
- const { logger, firestoreUtils } = dependencies;
69
- logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
70
-
71
- const {
72
- maxTasksPerRun,
73
- discoveryBatchSize,
74
- maxRandomCidsToDiscover,
75
- specBlocksCollection,
76
- pendingSpecCollection,
77
- normalUserCollection,
78
- invalidSpecCollection,
79
- speculatorInstrumentSet,
80
- snapshotsSubCollectionName,
81
- partsSubCollectionName
82
- } = config;
83
-
84
- const isSpeculator = userType === 'speculator';
85
- const dispatchedCids = new Set();
86
-
87
- // Pass dependencies and config to core util
88
- const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(
89
- dependencies,
90
- { normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName }
91
- );
92
- const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
93
-
94
- // Pass dependencies and config to core util
95
- const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
96
- specBlocksCollection,
97
- pendingSpecCollection,
98
- normalUserCollection,
99
- invalidSpecCollection,
100
- existingNormalUserIds
101
- }, userType);
102
-
103
- if (isSpeculator) {
104
- // Pass dependencies to core util
105
- const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(
106
- dependencies,
107
- exclusionIds,
108
- speculatorInstrumentSet,
109
- latestNormalPortfolios
110
- );
111
- logger.log('INFO', `Found ${prioritizedCandidates.length} potential new speculators from existing user pool.`);
112
-
113
- prioritizedCandidates.forEach(cidStr => {
114
- if (dispatchedCids.size < maxTasksPerRun) {
115
- dispatchedCids.add(cidStr);
116
- }
117
- });
118
- logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);
119
- }
120
-
121
- // --- Random Generation ---
122
- let dispatchedRandomCidCount = 0;
123
- const initialDispatchedCount = dispatchedCids.size;
124
-
125
- while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&
126
- dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0)
127
- {
128
- const blockIndex = dispatchedCids.size % blocksToFill.length;
129
- const block = blocksToFill[blockIndex];
130
- if (!block) break;
131
-
132
- for (let j = 0; j < discoveryBatchSize; j++) {
133
- if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
134
-
135
- let randomId;
136
- let retryCount = 0;
137
- const MAX_RETRIES = 50;
138
- do {
139
- if (++retryCount > MAX_RETRIES) break;
140
- randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
141
- } while (
142
- exclusionIds.has(randomId) ||
143
- dispatchedCids.has(randomId)
144
- );
145
-
146
- if (retryCount <= MAX_RETRIES) {
147
- dispatchedCids.add(randomId);
148
- dispatchedRandomCidCount++;
149
- }
150
- }
151
- }
152
-
153
- logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
154
- logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
155
- return dispatchedCids;
156
- }
157
-
158
- /**
159
- * Sub-pipe: pipe.orchestrator.dispatchDiscovery
160
- * @param {string} userType - 'normal' or 'speculator'.
161
- * @param {Set<string>} candidates - Set of candidate CIDs.
162
- * @param {object} config - The user type specific config.
163
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
164
- * @returns {Promise<void>}
165
- */
166
- async function dispatchDiscovery(userType, candidates, config, dependencies) {
167
- const { logger, firestoreUtils, pubsubUtils } = dependencies;
168
- const {
169
- topicName,
170
- dispatchBatchSize,
171
- pubsubBatchSize,
172
- pendingSpecCollection,
173
- pendingMaxFieldsPerDoc,
174
- pendingMaxWritesPerBatch
175
- } = config;
176
-
177
- if (candidates.size === 0) {
178
- logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
179
- return;
180
- }
181
- logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
182
-
183
- const isSpeculator = userType === 'speculator';
184
- const cidsArray = Array.from(candidates);
185
-
186
- if (isSpeculator) {
187
- await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
188
- await firestoreUtils.batchWriteShardedIds(dependencies, {
189
- collectionPath: pendingSpecCollection,
190
- items: cidsArray,
191
- timestamp: new Date(),
192
- maxFieldsPerDoc: pendingMaxFieldsPerDoc,
193
- maxWritesPerBatch: pendingMaxWritesPerBatch
194
- });
195
- }
196
-
197
- const tasks = [];
198
- for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
199
- const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
200
- if (batchCids.length > 0) {
201
- const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
202
- tasks.push({
203
- type: 'discover',
204
- cids: batchCids,
205
- blockId,
206
- userType
207
- });
208
- }
209
- }
210
-
211
- // Pass dependencies and config to core util
212
- await pubsubUtils.batchPublishTasks(dependencies, {
213
- topicName,
214
- tasks,
215
- taskType: `${userType} discovery`,
216
- maxPubsubBatchSize: pubsubBatchSize
217
- });
218
- logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${tasks.length} task messages (${candidates.size} CIDs) for ${userType} discovery.`);
219
- }
220
-
221
-
222
- module.exports = {
223
- checkDiscoveryNeed,
224
- getDiscoveryCandidates,
225
- dispatchDiscovery,
226
- };
1
+ /**
2
+ * @fileoverview Orchestrator's discovery sub-pipes.
3
+ * REFACTORED: All functions are now stateless and receive dependencies.
4
+ */
5
+
6
+ /**
7
+ * Sub-pipe: pipe.orchestrator.checkDiscoveryNeed
8
+ * @param {string} userType - 'normal' or 'speculator'.
9
+ * @param {object} config - The user type specific config (e.g., config.discoveryConfig.normal).
10
+ * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
11
+ * @returns {Promise<{needsDiscovery: boolean, blocksToFill: Array<object>}>}
12
+ */
13
+ async function checkDiscoveryNeed(userType, config, dependencies) {
14
+ const { logger, firestoreUtils } = dependencies;
15
+ logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
16
+
17
+ const {
18
+ targetUsersPerBlock,
19
+ speculatorBlockCountsDocPath,
20
+ normalBlockCountsDocPath,
21
+ speculatorInstruments,
22
+ allHighValueBlocks
23
+ } = config;
24
+
25
+ const isSpeculator = userType === 'speculator';
26
+
27
+ // Pass dependencies and config to core util
28
+ const blockCounts = await firestoreUtils.getBlockCapacities(
29
+ dependencies,
30
+ { speculatorBlockCountsDocPath, normalBlockCountsDocPath },
31
+ userType
32
+ );
33
+
34
+ let underPopulatedBlocks = [];
35
+ if (isSpeculator) {
36
+ for (const instrument of speculatorInstruments) {
37
+ for (const block of allHighValueBlocks) {
38
+ const instrumentBlockKey = `${instrument}_${block.startId}`;
39
+ if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {
40
+ underPopulatedBlocks.push({ ...block, instrument });
41
+ }
42
+ }
43
+ }
44
+ } else {
45
+ underPopulatedBlocks = allHighValueBlocks.filter(
46
+ block => (blockCounts[block.startId] || 0) < targetUsersPerBlock
47
+ );
48
+ }
49
+
50
+ const needsDiscovery = underPopulatedBlocks.length > 0;
51
+ if (!needsDiscovery) {
52
+ logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
53
+ } else {
54
+ logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);
55
+ }
56
+ return { needsDiscovery, blocksToFill: underPopulatedBlocks };
57
+ }
58
+
59
+ /**
60
+ * Sub-pipe: pipe.orchestrator.getDiscoveryCandidates
61
+ * @param {string} userType - 'normal' or 'speculator'.
62
+ * @param {Array<object>} blocksToFill - From checkDiscoveryNeed.
63
+ * @param {object} config - The user type specific config.
64
+ * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
65
+ * @returns {Promise<Set<string>>} Set of candidate CIDs.
66
+ */
67
+ async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
68
+ const { logger, firestoreUtils } = dependencies;
69
+ logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
70
+
71
+ const {
72
+ maxTasksPerRun,
73
+ discoveryBatchSize,
74
+ maxRandomCidsToDiscover,
75
+ specBlocksCollection,
76
+ pendingSpecCollection,
77
+ normalUserCollection,
78
+ invalidSpecCollection,
79
+ speculatorInstrumentSet,
80
+ snapshotsSubCollectionName,
81
+ partsSubCollectionName
82
+ } = config;
83
+
84
+ const isSpeculator = userType === 'speculator';
85
+ const dispatchedCids = new Set();
86
+
87
+ // Pass dependencies and config to core util
88
+ const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(
89
+ dependencies,
90
+ { normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName }
91
+ );
92
+ const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
93
+
94
+ // Pass dependencies and config to core util
95
+ const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
96
+ specBlocksCollection,
97
+ pendingSpecCollection,
98
+ normalUserCollection,
99
+ invalidSpecCollection,
100
+ existingNormalUserIds
101
+ }, userType);
102
+
103
+ if (isSpeculator) {
104
+ // Pass dependencies to core util
105
+ const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(
106
+ dependencies,
107
+ exclusionIds,
108
+ speculatorInstrumentSet,
109
+ latestNormalPortfolios
110
+ );
111
+ logger.log('INFO', `Found ${prioritizedCandidates.length} potential new speculators from existing user pool.`);
112
+
113
+ prioritizedCandidates.forEach(cidStr => {
114
+ if (dispatchedCids.size < maxTasksPerRun) {
115
+ dispatchedCids.add(cidStr);
116
+ }
117
+ });
118
+ logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);
119
+ }
120
+
121
+ // --- Random Generation ---
122
+ let dispatchedRandomCidCount = 0;
123
+ const initialDispatchedCount = dispatchedCids.size;
124
+
125
+ while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&
126
+ dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0)
127
+ {
128
+ const blockIndex = dispatchedCids.size % blocksToFill.length;
129
+ const block = blocksToFill[blockIndex];
130
+ if (!block) break;
131
+
132
+ for (let j = 0; j < discoveryBatchSize; j++) {
133
+ if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
134
+
135
+ let randomId;
136
+ let retryCount = 0;
137
+ const MAX_RETRIES = 50;
138
+ do {
139
+ if (++retryCount > MAX_RETRIES) break;
140
+ randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
141
+ } while (
142
+ exclusionIds.has(randomId) ||
143
+ dispatchedCids.has(randomId)
144
+ );
145
+
146
+ if (retryCount <= MAX_RETRIES) {
147
+ dispatchedCids.add(randomId);
148
+ dispatchedRandomCidCount++;
149
+ }
150
+ }
151
+ }
152
+
153
+ logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
154
+ logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
155
+ return dispatchedCids;
156
+ }
157
+
158
+ /**
159
+ * Sub-pipe: pipe.orchestrator.dispatchDiscovery
160
+ * @param {string} userType - 'normal' or 'speculator'.
161
+ * @param {Set<string>} candidates - Set of candidate CIDs.
162
+ * @param {object} config - The user type specific config.
163
+ * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
164
+ * @returns {Promise<void>}
165
+ */
166
+ async function dispatchDiscovery(userType, candidates, config, dependencies) {
167
+ const { logger, firestoreUtils, pubsubUtils } = dependencies;
168
+ const {
169
+ topicName,
170
+ dispatchBatchSize,
171
+ pubsubBatchSize,
172
+ pendingSpecCollection,
173
+ pendingMaxFieldsPerDoc,
174
+ pendingMaxWritesPerBatch
175
+ } = config;
176
+
177
+ if (candidates.size === 0) {
178
+ logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
179
+ return;
180
+ }
181
+ logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
182
+
183
+ const isSpeculator = userType === 'speculator';
184
+ const cidsArray = Array.from(candidates);
185
+
186
+ if (isSpeculator) {
187
+ await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
188
+ await firestoreUtils.batchWriteShardedIds(dependencies, {
189
+ collectionPath: pendingSpecCollection,
190
+ items: cidsArray,
191
+ timestamp: new Date(),
192
+ maxFieldsPerDoc: pendingMaxFieldsPerDoc,
193
+ maxWritesPerBatch: pendingMaxWritesPerBatch
194
+ });
195
+ }
196
+
197
+ const tasks = [];
198
+ for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
199
+ const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
200
+ if (batchCids.length > 0) {
201
+ const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
202
+ tasks.push({
203
+ type: 'discover',
204
+ cids: batchCids,
205
+ blockId,
206
+ userType
207
+ });
208
+ }
209
+ }
210
+
211
+ // Pass dependencies and config to core util
212
+ await pubsubUtils.batchPublishTasks(dependencies, {
213
+ topicName,
214
+ tasks,
215
+ taskType: `${userType} discovery`,
216
+ maxPubsubBatchSize: pubsubBatchSize
217
+ });
218
+ logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${tasks.length} task messages (${candidates.size} CIDs) for ${userType} discovery.`);
219
+ }
220
+
221
+
222
+ module.exports = {
223
+ checkDiscoveryNeed,
224
+ getDiscoveryCandidates,
225
+ dispatchDiscovery,
226
+ };
@@ -1,92 +1,92 @@
1
- /**
2
- * @fileoverview Orchestrator's update sub-pipes.
3
- * REFACTORED: All functions are now stateless and receive dependencies.
4
- */
5
-
6
- /**
7
- * Sub-pipe: pipe.orchestrator.getUpdateTargets
8
- * @param {string} userType - 'normal' or 'speculator'.
9
- * @param {object} thresholds - Contains date thresholds.
10
- * @param {object} config - The *global* updateConfig object.
11
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
12
- * @returns {Promise<Array<any>>} Array of targets.
13
- */
14
- async function getUpdateTargets(userType, thresholds, config, dependencies) {
15
- const { logger, firestoreUtils } = dependencies;
16
- logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
17
- let targets = [];
18
-
19
- try {
20
- if (userType === 'normal') {
21
- // Pass dependencies and config to core util
22
- targets = await firestoreUtils.getNormalUsersToUpdate(dependencies, {
23
- dateThreshold: thresholds.dateThreshold,
24
- normalUserCollectionName: config.normalUserCollectionName,
25
- normalUserTimestampsDocId: config.normalUserTimestampsDocId
26
- });
27
- } else if (userType === 'speculator') {
28
- // Pass dependencies and config to core util
29
- targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies, {
30
- dateThreshold: thresholds.dateThreshold,
31
- gracePeriodThreshold: thresholds.gracePeriodThreshold,
32
- speculatorBlocksCollectionName: config.speculatorBlocksCollectionName
33
- });
34
- }
35
- logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
36
- return targets;
37
- } catch (error) {
38
- logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message });
39
- throw error;
40
- }
41
- }
42
-
43
- /**
44
- * Sub-pipe: pipe.orchestrator.dispatchUpdates
45
- * @param {Array<any>} targets - Array of targets from getUpdateTargets.
46
- * @param {string} userType - 'normal' or 'speculator'.
47
- * @param {object} config - The *global* updateConfig object.
48
- * @param {object} dependencies - Contains db, pubsub, logger, pubsubUtils.
49
- * @returns {Promise<void>}
50
- */
51
- async function dispatchUpdates(targets, userType, config, dependencies) {
52
- const { logger, pubsubUtils } = dependencies;
53
- const { dispatcherTopicName, taskBatchSize, pubsubBatchSize } = config;
54
-
55
- if (targets.length === 0) {
56
- logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`);
57
- return;
58
- }
59
- logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
60
-
61
- const individualTasks = targets.map(target => ({
62
- type: 'update',
63
- userId: userType === 'normal' ? target : target.userId,
64
- instrumentId: userType ==='speculator' ? target.instrumentId : undefined,
65
- userType,
66
- }));
67
-
68
- const dispatcherMessages = [];
69
- for (let i = 0; i < individualTasks.length; i += taskBatchSize) {
70
- const batch = individualTasks.slice(i, i + taskBatchSize);
71
- dispatcherMessages.push({ tasks: batch }); // Wrap the batch
72
- }
73
-
74
- try {
75
- // Pass dependencies and config to core util
76
- await pubsubUtils.batchPublishTasks(dependencies, {
77
- topicName: dispatcherTopicName,
78
- tasks: dispatcherMessages,
79
- taskType: `${userType} update batch`,
80
- maxPubsubBatchSize: pubsubBatchSize
81
- });
82
- logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`);
83
- } catch (error) {
84
- logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message });
85
- throw error;
86
- }
87
- }
88
-
89
- module.exports = {
90
- getUpdateTargets,
91
- dispatchUpdates,
92
- };
1
+ /**
2
+ * @fileoverview Orchestrator's update sub-pipes.
3
+ * REFACTORED: All functions are now stateless and receive dependencies.
4
+ */
5
+
6
+ /**
7
+ * Sub-pipe: pipe.orchestrator.getUpdateTargets
8
+ * @param {string} userType - 'normal' or 'speculator'.
9
+ * @param {object} thresholds - Contains date thresholds.
10
+ * @param {object} config - The *global* updateConfig object.
11
+ * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
12
+ * @returns {Promise<Array<any>>} Array of targets.
13
+ */
14
+ async function getUpdateTargets(userType, thresholds, config, dependencies) {
15
+ const { logger, firestoreUtils } = dependencies;
16
+ logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
17
+ let targets = [];
18
+
19
+ try {
20
+ if (userType === 'normal') {
21
+ // Pass dependencies and config to core util
22
+ targets = await firestoreUtils.getNormalUsersToUpdate(dependencies, {
23
+ dateThreshold: thresholds.dateThreshold,
24
+ normalUserCollectionName: config.normalUserCollectionName,
25
+ normalUserTimestampsDocId: config.normalUserTimestampsDocId
26
+ });
27
+ } else if (userType === 'speculator') {
28
+ // Pass dependencies and config to core util
29
+ targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies, {
30
+ dateThreshold: thresholds.dateThreshold,
31
+ gracePeriodThreshold: thresholds.gracePeriodThreshold,
32
+ speculatorBlocksCollectionName: config.speculatorBlocksCollectionName
33
+ });
34
+ }
35
+ logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
36
+ return targets;
37
+ } catch (error) {
38
+ logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message });
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Sub-pipe: pipe.orchestrator.dispatchUpdates
45
+ * @param {Array<any>} targets - Array of targets from getUpdateTargets.
46
+ * @param {string} userType - 'normal' or 'speculator'.
47
+ * @param {object} config - The *global* updateConfig object.
48
+ * @param {object} dependencies - Contains db, pubsub, logger, pubsubUtils.
49
+ * @returns {Promise<void>}
50
+ */
51
+ async function dispatchUpdates(targets, userType, config, dependencies) {
52
+ const { logger, pubsubUtils } = dependencies;
53
+ const { dispatcherTopicName, taskBatchSize, pubsubBatchSize } = config;
54
+
55
+ if (targets.length === 0) {
56
+ logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`);
57
+ return;
58
+ }
59
+ logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
60
+
61
+ const individualTasks = targets.map(target => ({
62
+ type: 'update',
63
+ userId: userType === 'normal' ? target : target.userId,
64
+ instrumentId: userType ==='speculator' ? target.instrumentId : undefined,
65
+ userType,
66
+ }));
67
+
68
+ const dispatcherMessages = [];
69
+ for (let i = 0; i < individualTasks.length; i += taskBatchSize) {
70
+ const batch = individualTasks.slice(i, i + taskBatchSize);
71
+ dispatcherMessages.push({ tasks: batch }); // Wrap the batch
72
+ }
73
+
74
+ try {
75
+ // Pass dependencies and config to core util
76
+ await pubsubUtils.batchPublishTasks(dependencies, {
77
+ topicName: dispatcherTopicName,
78
+ tasks: dispatcherMessages,
79
+ taskType: `${userType} update batch`,
80
+ maxPubsubBatchSize: pubsubBatchSize
81
+ });
82
+ logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`);
83
+ } catch (error) {
84
+ logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message });
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ module.exports = {
90
+ getUpdateTargets,
91
+ dispatchUpdates,
92
+ };