bulltrackers-module 1.0.510 → 1.0.511
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/generic-api/user-api/helpers/core/path_resolution_helpers.js +34 -7
- package/functions/generic-api/user-api/helpers/search/pi_request_helpers.js +25 -0
- package/functions/task-engine/helpers/data_storage_helpers.js +13 -1
- package/functions/task-engine/helpers/popular_investor_helpers.js +66 -26
- package/package.json +1 -1
|
@@ -31,7 +31,7 @@ function getRegistryFunctions(options = {}) {
|
|
|
31
31
|
return {
|
|
32
32
|
getCollectionPath: options.collectionRegistry.getCollectionPath,
|
|
33
33
|
resolvePath: options.collectionRegistry.resolvePath || resolvePath,
|
|
34
|
-
getCollectionMetadata: options.collectionRegistry.getCollectionMetadata
|
|
34
|
+
getCollectionMetadata: options.collectionRegistry.getCollectionMetadata
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -254,6 +254,7 @@ async function readWithMigration(db, category, subcategory, params, options = {}
|
|
|
254
254
|
if (newPath) {
|
|
255
255
|
try {
|
|
256
256
|
if (isCollection) {
|
|
257
|
+
// Collection path must have odd number of segments
|
|
257
258
|
const collectionRef = db.collection(newPath);
|
|
258
259
|
const snapshot = await collectionRef.get();
|
|
259
260
|
if (!snapshot.empty) {
|
|
@@ -261,9 +262,21 @@ async function readWithMigration(db, category, subcategory, params, options = {}
|
|
|
261
262
|
return { snapshot, source: 'new', path: newPath };
|
|
262
263
|
}
|
|
263
264
|
} else {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
// Document path: if newPath has even segments, it's already a document path
|
|
266
|
+
// If documentId is provided, newPath should be collection path (odd segments)
|
|
267
|
+
let docRef;
|
|
268
|
+
if (documentId) {
|
|
269
|
+
// newPath should be collection (odd segments), add documentId
|
|
270
|
+
const pathSegments = newPath.split('/');
|
|
271
|
+
if (pathSegments.length % 2 === 0) {
|
|
272
|
+
// Even segments = document path, can't add documentId
|
|
273
|
+
throw new Error(`Path ${newPath} is a document path but documentId was provided`);
|
|
274
|
+
}
|
|
275
|
+
docRef = db.collection(newPath).doc(documentId);
|
|
276
|
+
} else {
|
|
277
|
+
// No documentId, newPath should be full document path (even segments)
|
|
278
|
+
docRef = db.doc(newPath);
|
|
279
|
+
}
|
|
267
280
|
const doc = await docRef.get();
|
|
268
281
|
if (doc.exists) {
|
|
269
282
|
if (logger) logger.log('INFO', `[readWithMigration] Found data in new path: ${docRef.path}`);
|
|
@@ -471,6 +484,11 @@ async function writeWithMigration(db, category, subcategory, params, data, optio
|
|
|
471
484
|
if (!documentId) {
|
|
472
485
|
throw new Error('Collection writes require documentId');
|
|
473
486
|
}
|
|
487
|
+
// Collection path must have odd number of segments
|
|
488
|
+
const pathSegments = newPath.split('/');
|
|
489
|
+
if (pathSegments.length % 2 === 0) {
|
|
490
|
+
throw new Error(`Path ${newPath} is a document path but isCollection=true was specified`);
|
|
491
|
+
}
|
|
474
492
|
const newRef = db.collection(newPath).doc(documentId);
|
|
475
493
|
if (merge) {
|
|
476
494
|
batch.set(newRef, data, { merge: true });
|
|
@@ -478,9 +496,18 @@ async function writeWithMigration(db, category, subcategory, params, data, optio
|
|
|
478
496
|
batch.set(newRef, data);
|
|
479
497
|
}
|
|
480
498
|
} else {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
499
|
+
// Document path: if documentId provided, newPath should be collection (odd segments)
|
|
500
|
+
// If no documentId, newPath should be full document path (even segments)
|
|
501
|
+
let newRef;
|
|
502
|
+
if (documentId) {
|
|
503
|
+
const pathSegments = newPath.split('/');
|
|
504
|
+
if (pathSegments.length % 2 === 0) {
|
|
505
|
+
throw new Error(`Path ${newPath} is a document path but documentId was provided`);
|
|
506
|
+
}
|
|
507
|
+
newRef = db.collection(newPath).doc(documentId);
|
|
508
|
+
} else {
|
|
509
|
+
newRef = db.doc(newPath);
|
|
510
|
+
}
|
|
484
511
|
if (merge) {
|
|
485
512
|
batch.set(newRef, data, { merge: true });
|
|
486
513
|
} else {
|
|
@@ -44,8 +44,33 @@ async function requestPiAddition(req, res, dependencies, config) {
|
|
|
44
44
|
.collection('requests')
|
|
45
45
|
.doc(requestId);
|
|
46
46
|
|
|
47
|
+
// Write to global collection
|
|
47
48
|
await requestRef.set(requestData);
|
|
48
49
|
|
|
50
|
+
// Also write to user-centric collection (dual-write)
|
|
51
|
+
const { writeWithMigration } = require('../core/path_resolution_helpers');
|
|
52
|
+
try {
|
|
53
|
+
await writeWithMigration(
|
|
54
|
+
db,
|
|
55
|
+
'signedInUsers',
|
|
56
|
+
'piAdditionRequests',
|
|
57
|
+
{ cid: userCid },
|
|
58
|
+
requestData,
|
|
59
|
+
{
|
|
60
|
+
isCollection: true,
|
|
61
|
+
merge: false,
|
|
62
|
+
dataType: 'piAdditionRequests',
|
|
63
|
+
config,
|
|
64
|
+
documentId: requestId,
|
|
65
|
+
dualWrite: false, // Don't dual-write to legacy (new feature)
|
|
66
|
+
collectionRegistry: dependencies.collectionRegistry
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
} catch (userWriteErr) {
|
|
70
|
+
// Log but don't fail - global write succeeded
|
|
71
|
+
logger.log('WARN', `[requestPiAddition] Failed to write to user-centric collection: ${userWriteErr.message}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
logger.log('SUCCESS', `[requestPiAddition] User ${userCid} requested addition of PI ${piUsername} (CID: ${piCid || 'unknown'})`);
|
|
50
75
|
|
|
51
76
|
return res.status(201).json({
|
|
@@ -36,6 +36,10 @@ async function storeSignedInUserPortfolio({ db, logger, collectionRegistry, cid,
|
|
|
36
36
|
}, { merge: false });
|
|
37
37
|
|
|
38
38
|
// 2. Store latest snapshot to user-centric collection (for fallback)
|
|
39
|
+
const { getCollectionPath } = collectionRegistry || {};
|
|
40
|
+
if (!getCollectionPath) {
|
|
41
|
+
throw new Error('collectionRegistry.getCollectionPath is required');
|
|
42
|
+
}
|
|
39
43
|
const userLatestRef = db.collection(
|
|
40
44
|
getCollectionPath('signedInUsers', 'portfolio', { cid })
|
|
41
45
|
).doc('latest');
|
|
@@ -76,6 +80,10 @@ async function storeSignedInUserTradeHistory({ db, logger, collectionRegistry, c
|
|
|
76
80
|
}, { merge: false });
|
|
77
81
|
|
|
78
82
|
// 2. Store latest snapshot to user-centric collection (for fallback)
|
|
83
|
+
const { getCollectionPath } = collectionRegistry || {};
|
|
84
|
+
if (!getCollectionPath) {
|
|
85
|
+
throw new Error('collectionRegistry.getCollectionPath is required');
|
|
86
|
+
}
|
|
79
87
|
const userLatestRef = db.collection(
|
|
80
88
|
getCollectionPath('signedInUsers', 'tradeHistory', { cid })
|
|
81
89
|
).doc('latest');
|
|
@@ -124,8 +132,12 @@ async function storeSignedInUserSocialPosts({ db, logger, collectionRegistry, ci
|
|
|
124
132
|
}, { merge: false });
|
|
125
133
|
|
|
126
134
|
// 2. Store latest posts to user-centric collection (for fallback)
|
|
135
|
+
const { getCollectionPath } = collectionRegistry || {};
|
|
136
|
+
if (!getCollectionPath) {
|
|
137
|
+
throw new Error('collectionRegistry.getCollectionPath is required');
|
|
138
|
+
}
|
|
127
139
|
const userPostsRef = db.collection(
|
|
128
|
-
getCollectionPath('signedInUsers', '
|
|
140
|
+
getCollectionPath('signedInUsers', 'socialPosts', { cid })
|
|
129
141
|
);
|
|
130
142
|
|
|
131
143
|
// Store each post individually in user-centric collection
|
|
@@ -21,7 +21,10 @@ const { shouldTryProxy, recordProxyOutcome, getFailureCount, getMaxFailures } =
|
|
|
21
21
|
const {
|
|
22
22
|
storePopularInvestorPortfolio,
|
|
23
23
|
storePopularInvestorTradeHistory,
|
|
24
|
-
storePopularInvestorSocialPosts
|
|
24
|
+
storePopularInvestorSocialPosts,
|
|
25
|
+
storeSignedInUserPortfolio,
|
|
26
|
+
storeSignedInUserTradeHistory,
|
|
27
|
+
storeSignedInUserSocialPosts
|
|
25
28
|
} = require('./data_storage_helpers');
|
|
26
29
|
|
|
27
30
|
async function handlePopularInvestorUpdate(taskData, config, dependencies) {
|
|
@@ -526,9 +529,10 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
|
526
529
|
// Extract targetCid from metadata if present (for optimization)
|
|
527
530
|
const targetCid = metadata?.targetCid || cid;
|
|
528
531
|
const isNewUser = metadata?.isNewUser === true; // Flag for new user signup
|
|
532
|
+
const userType = metadata?.userType || data.userType || 'SIGNED_IN_USER'; // Check if this is a PI or signed-in user
|
|
529
533
|
|
|
530
534
|
// [FIX] Destructure dependencies first
|
|
531
|
-
const { logger, proxyManager, batchManager, headerManager, db } = dependencies;
|
|
535
|
+
const { logger, proxyManager, batchManager, headerManager, db, collectionRegistry } = dependencies;
|
|
532
536
|
|
|
533
537
|
// Import notification helper once at the top
|
|
534
538
|
const { notifyTaskEngineComplete } = require('../../generic-api/user-api/helpers/notification_helpers');
|
|
@@ -687,14 +691,26 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
|
687
691
|
portfolioFetched = true;
|
|
688
692
|
|
|
689
693
|
try {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
694
|
+
// Use correct storage function based on user type
|
|
695
|
+
if (userType === 'POPULAR_INVESTOR') {
|
|
696
|
+
await storePopularInvestorPortfolio({
|
|
697
|
+
db,
|
|
698
|
+
logger,
|
|
699
|
+
collectionRegistry,
|
|
700
|
+
cid: String(cid),
|
|
701
|
+
date: today,
|
|
702
|
+
portfolioData
|
|
703
|
+
});
|
|
704
|
+
} else {
|
|
705
|
+
await storeSignedInUserPortfolio({
|
|
706
|
+
db,
|
|
707
|
+
logger,
|
|
708
|
+
collectionRegistry,
|
|
709
|
+
cid: String(cid),
|
|
710
|
+
date: today,
|
|
711
|
+
portfolioData
|
|
712
|
+
});
|
|
713
|
+
}
|
|
698
714
|
} catch (batchErr) {
|
|
699
715
|
const errorMsg = `Failed to store portfolio data: ${batchErr.message}`;
|
|
700
716
|
logger.log('ERROR', `[On-Demand] ${errorMsg}`, batchErr);
|
|
@@ -765,14 +781,26 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
|
765
781
|
}
|
|
766
782
|
|
|
767
783
|
try {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
784
|
+
// Use correct storage function based on user type
|
|
785
|
+
if (userType === 'POPULAR_INVESTOR') {
|
|
786
|
+
await storePopularInvestorTradeHistory({
|
|
787
|
+
db,
|
|
788
|
+
logger,
|
|
789
|
+
collectionRegistry,
|
|
790
|
+
cid: String(cid),
|
|
791
|
+
date: today,
|
|
792
|
+
historyData
|
|
793
|
+
});
|
|
794
|
+
} else {
|
|
795
|
+
await storeSignedInUserTradeHistory({
|
|
796
|
+
db,
|
|
797
|
+
logger,
|
|
798
|
+
collectionRegistry,
|
|
799
|
+
cid: String(cid),
|
|
800
|
+
date: today,
|
|
801
|
+
historyData
|
|
802
|
+
});
|
|
803
|
+
}
|
|
776
804
|
historyFetched = true;
|
|
777
805
|
} catch (batchErr) {
|
|
778
806
|
const errorMsg = `Failed to store history data: ${batchErr.message}`;
|
|
@@ -872,14 +900,26 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
|
872
900
|
|
|
873
901
|
// Store posts using new structure
|
|
874
902
|
if (postsToStore.length > 0) {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
903
|
+
// Use correct storage function based on user type
|
|
904
|
+
if (userType === 'POPULAR_INVESTOR') {
|
|
905
|
+
await storePopularInvestorSocialPosts({
|
|
906
|
+
db,
|
|
907
|
+
logger,
|
|
908
|
+
collectionRegistry,
|
|
909
|
+
cid: String(cid),
|
|
910
|
+
date: today,
|
|
911
|
+
posts: postsToStore
|
|
912
|
+
});
|
|
913
|
+
} else {
|
|
914
|
+
await storeSignedInUserSocialPosts({
|
|
915
|
+
db,
|
|
916
|
+
logger,
|
|
917
|
+
collectionRegistry,
|
|
918
|
+
cid: String(cid),
|
|
919
|
+
date: today,
|
|
920
|
+
posts: postsToStore
|
|
921
|
+
});
|
|
922
|
+
}
|
|
883
923
|
|
|
884
924
|
// Update date tracking document (for root data indexer)
|
|
885
925
|
const { getCollectionPath } = collectionRegistry;
|