bulltrackers-module 1.0.504 → 1.0.506
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/ADDING_LEGACY_ROUTES_GUIDE.md +345 -0
- package/functions/generic-api/user-api/CODE_REORGANIZATION_PLAN.md +320 -0
- package/functions/generic-api/user-api/COMPLETE_REFACTORING_PLAN.md +116 -0
- package/functions/generic-api/user-api/FIRESTORE_PATHS_INVENTORY.md +171 -0
- package/functions/generic-api/user-api/FIRESTORE_PATH_MIGRATION_REFERENCE.md +710 -0
- package/functions/generic-api/user-api/FIRESTORE_PATH_VALIDATION.md +109 -0
- package/functions/generic-api/user-api/MIGRATION_PLAN.md +499 -0
- package/functions/generic-api/user-api/README_MIGRATION.md +152 -0
- package/functions/generic-api/user-api/REFACTORING_COMPLETE.md +106 -0
- package/functions/generic-api/user-api/REFACTORING_STATUS.md +85 -0
- package/functions/generic-api/user-api/VERIFICATION_MIGRATION_NOTES.md +206 -0
- package/functions/generic-api/user-api/helpers/ORGANIZATION_COMPLETE.md +126 -0
- package/functions/generic-api/user-api/helpers/alerts/subscription_helpers.js +327 -0
- package/functions/generic-api/user-api/helpers/{test_alert_helpers.js → alerts/test_alert_helpers.js} +1 -1
- package/functions/generic-api/user-api/helpers/collection_helpers.js +23 -45
- package/functions/generic-api/user-api/helpers/core/compression_helpers.js +68 -0
- package/functions/generic-api/user-api/helpers/core/data_lookup_helpers.js +213 -0
- package/functions/generic-api/user-api/helpers/core/path_resolution_helpers.js +486 -0
- package/functions/generic-api/user-api/helpers/core/user_status_helpers.js +77 -0
- package/functions/generic-api/user-api/helpers/data/computation_helpers.js +299 -0
- package/functions/generic-api/user-api/helpers/data/instrument_helpers.js +55 -0
- package/functions/generic-api/user-api/helpers/data/portfolio_helpers.js +238 -0
- package/functions/generic-api/user-api/helpers/data/social_helpers.js +55 -0
- package/functions/generic-api/user-api/helpers/data_helpers.js +85 -2750
- package/functions/generic-api/user-api/helpers/{dev_helpers.js → dev/dev_helpers.js} +0 -1
- package/functions/generic-api/user-api/helpers/{on_demand_fetch_helpers.js → fetch/on_demand_fetch_helpers.js} +33 -115
- package/functions/generic-api/user-api/helpers/metrics/personalized_metrics_helpers.js +360 -0
- package/functions/generic-api/user-api/helpers/{notification_helpers.js → notifications/notification_helpers.js} +0 -1
- package/functions/generic-api/user-api/helpers/profile/pi_profile_helpers.js +200 -0
- package/functions/generic-api/user-api/helpers/profile/profile_view_helpers.js +125 -0
- package/functions/generic-api/user-api/helpers/profile/user_profile_helpers.js +178 -0
- package/functions/generic-api/user-api/helpers/recommendations/recommendation_helpers.js +65 -0
- package/functions/generic-api/user-api/helpers/{review_helpers.js → reviews/review_helpers.js} +23 -107
- package/functions/generic-api/user-api/helpers/search/pi_request_helpers.js +177 -0
- package/functions/generic-api/user-api/helpers/search/pi_search_helpers.js +70 -0
- package/functions/generic-api/user-api/helpers/{user_sync_helpers.js → sync/user_sync_helpers.js} +54 -127
- package/functions/generic-api/user-api/helpers/{verification_helpers.js → verification/verification_helpers.js} +4 -43
- package/functions/generic-api/user-api/helpers/watchlist/watchlist_analytics_helpers.js +95 -0
- package/functions/generic-api/user-api/helpers/watchlist/watchlist_data_helpers.js +139 -0
- package/functions/generic-api/user-api/helpers/watchlist/watchlist_generation_helpers.js +306 -0
- package/functions/generic-api/user-api/helpers/{watchlist_helpers.js → watchlist/watchlist_management_helpers.js} +62 -213
- package/functions/generic-api/user-api/index.js +9 -9
- package/functions/task-engine/handler_creator.js +7 -6
- package/package.json +1 -1
- package/functions/generic-api/API_MIGRATION_PLAN.md +0 -436
- package/functions/generic-api/user-api/helpers/FALLBACK_CONDITIONS.md +0 -98
- package/functions/generic-api/user-api/helpers/HISTORY_STORAGE_LOCATION.md +0 -66
- package/functions/generic-api/user-api/helpers/subscription_helpers.js +0 -512
- /package/functions/generic-api/user-api/helpers/{alert_helpers.js → alerts/alert_helpers.js} +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Adding New Legacy Routes for Auto-Migration
|
|
2
|
+
|
|
3
|
+
## How Easy Is It?
|
|
4
|
+
|
|
5
|
+
**Answer: Very Easy!** Adding a new legacy route requires updating **just 1 location** in most cases!
|
|
6
|
+
|
|
7
|
+
**Even Easier:** The system now automatically reads legacy paths from the collection registry, so you typically only need to add one line to the registry!
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Overview
|
|
12
|
+
|
|
13
|
+
The auto-migration system works by:
|
|
14
|
+
1. **Trying new path first** - Reads from the new CID-based path
|
|
15
|
+
2. **Falling back to legacy** - If not found, tries legacy path(s) from registry
|
|
16
|
+
3. **Auto-migrating** - If found in legacy, automatically migrates to new path
|
|
17
|
+
|
|
18
|
+
To add a new legacy route, you typically only need to:
|
|
19
|
+
1. **`collection_registry.js`** - Add to `legacyPaths` array (that's it!)
|
|
20
|
+
|
|
21
|
+
**Only if you need complex conditional logic:**
|
|
22
|
+
2. **`path_resolution_helpers.js`** - Add to `legacyPathMap` object (fallback)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Step-by-Step Guide
|
|
27
|
+
|
|
28
|
+
### ✅ EASIEST WAY: Just Add to Collection Registry (Recommended)
|
|
29
|
+
|
|
30
|
+
**File:** `Backend/Entrypoints/BullTrackers/Backend/Core/config/collection_registry.js`
|
|
31
|
+
|
|
32
|
+
**Location:** Find your collection definition and add to `legacyPaths` array
|
|
33
|
+
|
|
34
|
+
**Example:**
|
|
35
|
+
```javascript
|
|
36
|
+
notifications: {
|
|
37
|
+
path: 'SignedInUsers/{cid}/notifications',
|
|
38
|
+
description: 'User notifications',
|
|
39
|
+
dynamicSegments: ['{cid}'],
|
|
40
|
+
status: 'active',
|
|
41
|
+
legacyPaths: [
|
|
42
|
+
'user_notifications/{firebaseUid}/notifications', // Existing
|
|
43
|
+
'user_notifications/{userCid}/notifications', // Existing
|
|
44
|
+
'old_notifications/{userId}/items' // NEW - Just add here!
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**That's it!** The system automatically uses the first legacy path from the registry. No other changes needed!
|
|
50
|
+
|
|
51
|
+
**How it works:**
|
|
52
|
+
- `readWithMigration()` automatically passes `category` and `subcategory` to `getLegacyPath()`
|
|
53
|
+
- `getLegacyPath()` first tries to get legacy paths from the registry
|
|
54
|
+
- If found, it automatically resolves dynamic segments and returns the path
|
|
55
|
+
- No need to update `path_resolution_helpers.js`!
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Alternative: Add to Legacy Path Map (For Complex Logic)
|
|
60
|
+
|
|
61
|
+
**Only needed if:** You need conditional logic or special handling that can't be expressed in a simple path template.
|
|
62
|
+
|
|
63
|
+
**File:** `Backend/Entrypoints/BullTrackers/Backend/Core/bulltrackers-module/functions/generic-api/user-api/helpers/core/path_resolution_helpers.js`
|
|
64
|
+
|
|
65
|
+
**Location:** Inside `getLegacyPath()` function, add to `legacyPathMap` object
|
|
66
|
+
|
|
67
|
+
**Example:**
|
|
68
|
+
```javascript
|
|
69
|
+
function getLegacyPath(dataType, userCid, config = {}, params = {}) {
|
|
70
|
+
// ... registry lookup happens first ...
|
|
71
|
+
|
|
72
|
+
// Fallback to hardcoded map (only if registry doesn't have it)
|
|
73
|
+
const legacyPathMap = {
|
|
74
|
+
// ... existing mappings ...
|
|
75
|
+
|
|
76
|
+
// NEW - Add your new data type here (only if you need complex logic)
|
|
77
|
+
newDataType: params.someCondition
|
|
78
|
+
? `old_collection/${userId}/items/${params.someParam}`
|
|
79
|
+
: `old_collection/${userId}/items`
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return legacyPathMap[dataType] || null;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Note:** This is only needed for complex conditional logic. For simple paths, just use the registry!
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Real-World Example
|
|
91
|
+
|
|
92
|
+
Let's say you want to add support for a legacy "user preferences" collection:
|
|
93
|
+
|
|
94
|
+
### Step 1: Update Collection Registry
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// In collection_registry.js
|
|
98
|
+
signedInUsers: {
|
|
99
|
+
// ... other collections ...
|
|
100
|
+
|
|
101
|
+
preferences: {
|
|
102
|
+
path: 'SignedInUsers/{cid}/preferences/data',
|
|
103
|
+
description: 'User preferences (single document)',
|
|
104
|
+
dynamicSegments: ['{cid}'],
|
|
105
|
+
status: 'active',
|
|
106
|
+
legacyPaths: [
|
|
107
|
+
'user_preferences/{userCid}', // Add this
|
|
108
|
+
'old_prefs/{userId}/settings' // And this if multiple legacy formats exist
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Step 2: (Optional) Update Path Resolution Helper
|
|
115
|
+
|
|
116
|
+
**Only needed if:** You need conditional logic that can't be expressed in a simple path template.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
// In path_resolution_helpers.js
|
|
120
|
+
// This is only needed for complex conditional logic
|
|
121
|
+
// The registry lookup happens first, so this is just a fallback
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 3: Use It in Your API
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// In your API helper file
|
|
128
|
+
const { readWithMigration } = require('./helpers/core/path_resolution_helpers');
|
|
129
|
+
|
|
130
|
+
async function getUserPreferences(req, res, dependencies, config) {
|
|
131
|
+
const { db, logger } = dependencies;
|
|
132
|
+
const { userCid } = req.query;
|
|
133
|
+
|
|
134
|
+
const result = await readWithMigration(
|
|
135
|
+
db,
|
|
136
|
+
'signedInUsers', // category
|
|
137
|
+
'preferences', // subcategory
|
|
138
|
+
{ cid: userCid }, // params
|
|
139
|
+
{
|
|
140
|
+
isCollection: false, // It's a document
|
|
141
|
+
dataType: 'preferences', // This triggers legacy path lookup
|
|
142
|
+
config: config,
|
|
143
|
+
logger: logger
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
if (!result) {
|
|
148
|
+
return res.status(404).json({ error: "Preferences not found" });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// result.data contains the preferences
|
|
152
|
+
// result.source tells you if it came from 'new' or 'legacy'
|
|
153
|
+
// result.migrated tells you if it was auto-migrated
|
|
154
|
+
|
|
155
|
+
return res.status(200).json({
|
|
156
|
+
preferences: result.data,
|
|
157
|
+
source: result.source,
|
|
158
|
+
migrated: result.migrated || false
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**That's it!** The auto-migration happens automatically.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Advanced: Multiple Legacy Formats
|
|
168
|
+
|
|
169
|
+
If you have multiple legacy formats for the same data type:
|
|
170
|
+
|
|
171
|
+
### Option 1: Try Multiple Legacy Paths (Recommended)
|
|
172
|
+
|
|
173
|
+
Update `getLegacyPath()` to return an array or try multiple paths:
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
function getLegacyPath(dataType, userCid, config = {}, params = {}) {
|
|
177
|
+
const cid = String(userCid);
|
|
178
|
+
|
|
179
|
+
const legacyPathMap = {
|
|
180
|
+
// ... existing ...
|
|
181
|
+
|
|
182
|
+
preferences: [
|
|
183
|
+
`user_preferences/${cid}`, // Try first
|
|
184
|
+
`old_prefs/${cid}/settings`, // Try second
|
|
185
|
+
`legacy_preferences/${cid}/data` // Try third
|
|
186
|
+
]
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const path = legacyPathMap[dataType];
|
|
190
|
+
return Array.isArray(path) ? path[0] : path; // Return first for now
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Then update `readWithMigration()` to try all paths in the array (this would require a small code change).
|
|
195
|
+
|
|
196
|
+
### Option 2: Use Conditional Logic
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
const legacyPathMap = {
|
|
200
|
+
preferences: params.useOldFormat
|
|
201
|
+
? `old_prefs/${userId}/settings`
|
|
202
|
+
: `user_preferences/${cid}`
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Dynamic Segment Resolution
|
|
209
|
+
|
|
210
|
+
The system automatically resolves common dynamic segments:
|
|
211
|
+
|
|
212
|
+
- `{cid}` → User CID
|
|
213
|
+
- `{userCid}` → User CID
|
|
214
|
+
- `{firebaseUid}` → Firebase UID (from params)
|
|
215
|
+
- `{username}` → Username (from params)
|
|
216
|
+
- `{date}` → Date (from params)
|
|
217
|
+
- `{requestId}` → Request ID (from params or documentId)
|
|
218
|
+
- `{piCid}` → PI CID (from params)
|
|
219
|
+
- `{postId}` → Post ID (from params or documentId)
|
|
220
|
+
- `{watchlistId}` → Watchlist ID (from params)
|
|
221
|
+
- `{version}` → Version (from params)
|
|
222
|
+
- `{viewId}` → View ID (from params or documentId)
|
|
223
|
+
- `{reviewId}` → Review ID (from params or documentId)
|
|
224
|
+
- `{timestamp}` → Timestamp (from params or auto-generated)
|
|
225
|
+
|
|
226
|
+
**Just use these placeholders in your legacy path string!**
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Testing Your New Legacy Route
|
|
231
|
+
|
|
232
|
+
1. **Create test data in legacy path:**
|
|
233
|
+
```javascript
|
|
234
|
+
await db.collection('user_preferences').doc('29312236').set({
|
|
235
|
+
theme: 'dark',
|
|
236
|
+
notifications: true
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
2. **Call your API:**
|
|
241
|
+
```javascript
|
|
242
|
+
GET /user/me/preferences?userCid=29312236
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
3. **Check the response:**
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"preferences": { "theme": "dark", "notifications": true },
|
|
249
|
+
"source": "legacy",
|
|
250
|
+
"migrated": true
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
4. **Verify migration:**
|
|
255
|
+
```javascript
|
|
256
|
+
const newDoc = await db.collection('SignedInUsers/29312236/preferences/data').get();
|
|
257
|
+
// Should exist with _migratedAt and _migratedFrom fields
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Common Patterns
|
|
263
|
+
|
|
264
|
+
### Pattern 1: Simple CID-based Legacy Path
|
|
265
|
+
```javascript
|
|
266
|
+
legacyPaths: ['old_collection/{userCid}']
|
|
267
|
+
legacyPathMap: { newDataType: `old_collection/${cid}` }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Pattern 2: Firebase UID-based Legacy Path
|
|
271
|
+
```javascript
|
|
272
|
+
legacyPaths: ['old_collection/{firebaseUid}']
|
|
273
|
+
legacyPathMap: {
|
|
274
|
+
newDataType: firebaseUid
|
|
275
|
+
? `old_collection/${firebaseUid}`
|
|
276
|
+
: `old_collection/${cid}`
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Pattern 3: Nested Legacy Path
|
|
281
|
+
```javascript
|
|
282
|
+
legacyPaths: ['old_collection/{userCid}/subcollection/{itemId}']
|
|
283
|
+
legacyPathMap: {
|
|
284
|
+
newDataType: `old_collection/${cid}/subcollection/${params.itemId || '{itemId}'}`
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Pattern 4: Date-based Legacy Path
|
|
289
|
+
```javascript
|
|
290
|
+
legacyPaths: ['old_collection/{date}/{userCid}']
|
|
291
|
+
legacyPathMap: {
|
|
292
|
+
newDataType: `old_collection/${params.date || '{date}'}/${cid}`
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Troubleshooting
|
|
299
|
+
|
|
300
|
+
### Legacy Path Not Found?
|
|
301
|
+
|
|
302
|
+
1. **Check `dataType` parameter** - Must match key in `legacyPathMap`
|
|
303
|
+
2. **Check `params`** - Ensure all required dynamic segments are provided
|
|
304
|
+
3. **Check path resolution** - Use logger to see what path is being tried
|
|
305
|
+
|
|
306
|
+
### Migration Not Happening?
|
|
307
|
+
|
|
308
|
+
1. **Check if new path exists** - Migration only happens if new path is available
|
|
309
|
+
2. **Check logger** - Look for migration success/error messages
|
|
310
|
+
3. **Check permissions** - Ensure write permissions to new path
|
|
311
|
+
|
|
312
|
+
### Multiple Legacy Formats?
|
|
313
|
+
|
|
314
|
+
1. **Add all to `legacyPaths` array** in registry
|
|
315
|
+
2. **Use conditional logic** in `getLegacyPath()` to choose which to try first
|
|
316
|
+
3. **Or modify `readWithMigration()`** to try multiple legacy paths sequentially
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Summary
|
|
321
|
+
|
|
322
|
+
**Adding a new legacy route is as simple as:**
|
|
323
|
+
|
|
324
|
+
### ✅ EASIEST (Recommended):
|
|
325
|
+
1. ✅ Add one line to `legacyPaths` array in `collection_registry.js`
|
|
326
|
+
2. ✅ Use `readWithMigration()` in your API code (pass `category` and `subcategory`)
|
|
327
|
+
|
|
328
|
+
**Total time: ~30 seconds per legacy route!**
|
|
329
|
+
|
|
330
|
+
### Alternative (For Complex Logic):
|
|
331
|
+
1. ✅ Add one line to `legacyPaths` array in `collection_registry.js` (optional, for documentation)
|
|
332
|
+
2. ✅ Add one line to `legacyPathMap` in `path_resolution_helpers.js` (if you need conditional logic)
|
|
333
|
+
3. ✅ Use `readWithMigration()` in your API code
|
|
334
|
+
|
|
335
|
+
**Total time: ~2 minutes per legacy route!**
|
|
336
|
+
|
|
337
|
+
The system handles:
|
|
338
|
+
- ✅ Path resolution
|
|
339
|
+
- ✅ Fallback logic
|
|
340
|
+
- ✅ Auto-migration
|
|
341
|
+
- ✅ Error handling
|
|
342
|
+
- ✅ Logging
|
|
343
|
+
|
|
344
|
+
You just need to define the paths! 🎉
|
|
345
|
+
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# User API Code Reorganization Plan
|
|
2
|
+
|
|
3
|
+
## Current State Analysis
|
|
4
|
+
|
|
5
|
+
### File Sizes and Complexity
|
|
6
|
+
- **data_helpers.js**: 2573 lines, 29 functions - **TOO LARGE**
|
|
7
|
+
- **review_helpers.js**: 545 lines, 4 functions - OK
|
|
8
|
+
- **watchlist_helpers.js**: 697 lines, 8 functions - OK
|
|
9
|
+
- **on_demand_fetch_helpers.js**: 554 lines, 3 functions - OK
|
|
10
|
+
- **user_sync_helpers.js**: 593 lines, 2 functions - OK
|
|
11
|
+
- **alert_helpers.js**: 355 lines, 7 functions - OK
|
|
12
|
+
- **subscription_helpers.js**: 328 lines, 5 functions - OK
|
|
13
|
+
- **verification_helpers.js**: 231 lines, 2 functions - OK
|
|
14
|
+
- **notification_helpers.js**: 131 lines, 3 functions - OK
|
|
15
|
+
- **dev_helpers.js**: 316 lines, 6 functions - OK
|
|
16
|
+
- **collection_helpers.js**: 216 lines, 8 functions - OK
|
|
17
|
+
|
|
18
|
+
### Functions in data_helpers.js (29 total)
|
|
19
|
+
1. `tryDecompress` - Utility
|
|
20
|
+
2. `findLatestPortfolioDate` - Data lookup utility
|
|
21
|
+
3. `findLatestComputationDate` - Data lookup utility
|
|
22
|
+
4. `checkIfUserIsPI` - User status check
|
|
23
|
+
5. `findLatestRankingsDate` - Data lookup utility
|
|
24
|
+
6. `findLatestPiPortfolioDate` - Data lookup utility
|
|
25
|
+
7. `findLatestPiHistoryDate` - Data lookup utility
|
|
26
|
+
8. `getPiAnalytics` - PI analytics endpoint
|
|
27
|
+
9. `getUserRecommendations` - User recommendations endpoint
|
|
28
|
+
10. `getUserDataStatus` - User data status endpoint
|
|
29
|
+
11. `getWatchlist` - Watchlist endpoint (legacy)
|
|
30
|
+
12. `updateWatchlist` - Watchlist endpoint (legacy)
|
|
31
|
+
13. `getUserPortfolio` - User portfolio endpoint
|
|
32
|
+
14. `getUserSocialPosts` - User social posts endpoint
|
|
33
|
+
15. `getInstrumentMappings` - Instrument mappings endpoint
|
|
34
|
+
16. `getUserVerification` - User verification endpoint
|
|
35
|
+
17. `getUserComputations` - User computations endpoint
|
|
36
|
+
18. `autoGenerateWatchlist` - Watchlist generation endpoint
|
|
37
|
+
19. `searchPopularInvestors` - PI search endpoint
|
|
38
|
+
20. `requestPiAddition` - PI request endpoint
|
|
39
|
+
21. `getWatchlistTriggerCounts` - Watchlist analytics endpoint
|
|
40
|
+
22. `checkPisInRankings` - PI rankings check endpoint
|
|
41
|
+
23. `checkPiInComputationDate` - Computation check utility
|
|
42
|
+
24. `getPiProfile` - PI profile endpoint
|
|
43
|
+
25. `checkIfUserIsPopularInvestor` - User status endpoint
|
|
44
|
+
26. `trackProfileView` - Profile view tracking endpoint
|
|
45
|
+
27. `generateSamplePIPersonalizedMetrics` - Sample data generator
|
|
46
|
+
28. `getSignedInUserPIPersonalizedMetrics` - Personalized metrics endpoint
|
|
47
|
+
|
|
48
|
+
## Proposed Reorganization
|
|
49
|
+
|
|
50
|
+
### New File Structure
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
user-api/
|
|
54
|
+
├── index.js (router - unchanged)
|
|
55
|
+
├── helpers/
|
|
56
|
+
│ ├── core/
|
|
57
|
+
│ │ ├── compression_helpers.js (tryDecompress)
|
|
58
|
+
│ │ ├── data_lookup_helpers.js (findLatest* functions)
|
|
59
|
+
│ │ ├── user_status_helpers.js (checkIfUserIsPI, checkIfUserIsPopularInvestor)
|
|
60
|
+
│ │ └── path_resolution_helpers.js (enhanced collection_helpers)
|
|
61
|
+
│ ├── data/
|
|
62
|
+
│ │ ├── portfolio_helpers.js (getUserPortfolio, getUserDataStatus)
|
|
63
|
+
│ │ ├── social_helpers.js (getUserSocialPosts)
|
|
64
|
+
│ │ ├── computation_helpers.js (getUserComputations, checkPiInComputationDate)
|
|
65
|
+
│ │ └── instrument_helpers.js (getInstrumentMappings)
|
|
66
|
+
│ ├── profile/
|
|
67
|
+
│ │ ├── pi_profile_helpers.js (getPiProfile, getPiAnalytics)
|
|
68
|
+
│ │ ├── user_profile_helpers.js (getUserVerification, checkIfUserIsPopularInvestor)
|
|
69
|
+
│ │ └── profile_view_helpers.js (trackProfileView)
|
|
70
|
+
│ ├── watchlist/
|
|
71
|
+
│ │ ├── watchlist_data_helpers.js (getWatchlist, updateWatchlist - legacy)
|
|
72
|
+
│ │ ├── watchlist_generation_helpers.js (autoGenerateWatchlist)
|
|
73
|
+
│ │ └── watchlist_analytics_helpers.js (getWatchlistTriggerCounts)
|
|
74
|
+
│ ├── recommendations/
|
|
75
|
+
│ │ └── recommendation_helpers.js (getUserRecommendations)
|
|
76
|
+
│ ├── search/
|
|
77
|
+
│ │ ├── pi_search_helpers.js (searchPopularInvestors)
|
|
78
|
+
│ │ └── pi_request_helpers.js (requestPiAddition, checkPisInRankings)
|
|
79
|
+
│ ├── metrics/
|
|
80
|
+
│ │ └── personalized_metrics_helpers.js (getSignedInUserPIPersonalizedMetrics, generateSamplePIPersonalizedMetrics)
|
|
81
|
+
│ ├── notifications/
|
|
82
|
+
│ │ └── notification_helpers.js (unchanged)
|
|
83
|
+
│ ├── alerts/
|
|
84
|
+
│ │ └── alert_helpers.js (unchanged)
|
|
85
|
+
│ ├── watchlists/
|
|
86
|
+
│ │ └── watchlist_helpers.js (unchanged)
|
|
87
|
+
│ ├── subscriptions/
|
|
88
|
+
│ │ └── subscription_helpers.js (unchanged)
|
|
89
|
+
│ ├── verification/
|
|
90
|
+
│ │ └── verification_helpers.js (unchanged)
|
|
91
|
+
│ ├── reviews/
|
|
92
|
+
│ │ └── review_helpers.js (unchanged)
|
|
93
|
+
│ ├── sync/
|
|
94
|
+
│ │ └── user_sync_helpers.js (unchanged)
|
|
95
|
+
│ ├── fetch/
|
|
96
|
+
│ │ └── on_demand_fetch_helpers.js (unchanged)
|
|
97
|
+
│ ├── dev/
|
|
98
|
+
│ │ └── dev_helpers.js (unchanged)
|
|
99
|
+
│ └── collection_helpers.js (keep for backward compatibility, re-export from core/)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Detailed Breakdown
|
|
103
|
+
|
|
104
|
+
### 1. core/compression_helpers.js
|
|
105
|
+
**Purpose:** Compression/decompression utilities
|
|
106
|
+
**Functions:**
|
|
107
|
+
- `tryDecompress(data)` - Decompress computation results
|
|
108
|
+
|
|
109
|
+
**Dependencies:** `zlib`
|
|
110
|
+
|
|
111
|
+
### 2. core/data_lookup_helpers.js
|
|
112
|
+
**Purpose:** Find latest available data dates
|
|
113
|
+
**Functions:**
|
|
114
|
+
- `findLatestPortfolioDate(db, signedInUsersCollection, userCid, maxDaysBack)`
|
|
115
|
+
- `findLatestComputationDate(db, insightsCollection, resultsSub, compsSub, category, computationName, userCid, maxDaysBack)`
|
|
116
|
+
- `findLatestRankingsDate(db, rankingsCollection, maxDaysBack)`
|
|
117
|
+
- `findLatestPiPortfolioDate(db, piPortfoliosCollection, userCid, maxDaysBack)`
|
|
118
|
+
- `findLatestPiHistoryDate(db, piHistoryCollection, userCid, maxDaysBack)`
|
|
119
|
+
|
|
120
|
+
**Dependencies:** `@google-cloud/firestore`
|
|
121
|
+
|
|
122
|
+
### 3. core/user_status_helpers.js
|
|
123
|
+
**Purpose:** Check user status (PI, signed-in, etc.)
|
|
124
|
+
**Functions:**
|
|
125
|
+
- `checkIfUserIsPI(db, userCid, config, logger)`
|
|
126
|
+
- `checkIfUserIsPopularInvestor(req, res, dependencies, config)` (endpoint handler)
|
|
127
|
+
|
|
128
|
+
**Dependencies:** `dev_helpers`, `data_lookup_helpers`
|
|
129
|
+
|
|
130
|
+
### 4. core/path_resolution_helpers.js
|
|
131
|
+
**Purpose:** Enhanced path resolution with migration support
|
|
132
|
+
**Functions:**
|
|
133
|
+
- `getCidFromFirebaseUid(db, firebaseUid)` - from collection_helpers
|
|
134
|
+
- `getCollectionPath(collectionRegistry, category, subcategory, params)` - enhanced
|
|
135
|
+
- `readWithMigration(db, newPath, legacyPath, options)` - new, with auto-migration
|
|
136
|
+
- `writeDual(db, newPath, legacyPath, data, options)` - enhanced
|
|
137
|
+
- `migrateUserData(db, userCid, dataType, logger)` - new, batch migration
|
|
138
|
+
|
|
139
|
+
**Dependencies:** `collection_registry`, `@google-cloud/firestore`
|
|
140
|
+
|
|
141
|
+
### 5. data/portfolio_helpers.js
|
|
142
|
+
**Purpose:** User portfolio data endpoints
|
|
143
|
+
**Functions:**
|
|
144
|
+
- `getUserPortfolio(req, res, dependencies, config)`
|
|
145
|
+
- `getUserDataStatus(req, res, dependencies, config)`
|
|
146
|
+
|
|
147
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/path_resolution_helpers`, `core/compression_helpers`
|
|
148
|
+
|
|
149
|
+
### 6. data/social_helpers.js
|
|
150
|
+
**Purpose:** User social posts endpoints
|
|
151
|
+
**Functions:**
|
|
152
|
+
- `getUserSocialPosts(req, res, dependencies, config)`
|
|
153
|
+
|
|
154
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/path_resolution_helpers`, `core/compression_helpers`
|
|
155
|
+
|
|
156
|
+
### 7. data/computation_helpers.js
|
|
157
|
+
**Purpose:** Computation result endpoints
|
|
158
|
+
**Functions:**
|
|
159
|
+
- `getUserComputations(req, res, dependencies, config)`
|
|
160
|
+
- `checkPiInComputationDate(db, insightsCollection, resultsSub, compsSub, category, computationName, dateStr, cidStr, logger)`
|
|
161
|
+
|
|
162
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `core/path_resolution_helpers`
|
|
163
|
+
|
|
164
|
+
### 8. data/instrument_helpers.js
|
|
165
|
+
**Purpose:** Instrument mappings endpoint
|
|
166
|
+
**Functions:**
|
|
167
|
+
- `getInstrumentMappings(req, res, dependencies, config)`
|
|
168
|
+
|
|
169
|
+
**Dependencies:** `@google-cloud/firestore`
|
|
170
|
+
|
|
171
|
+
### 9. profile/pi_profile_helpers.js
|
|
172
|
+
**Purpose:** Popular Investor profile endpoints
|
|
173
|
+
**Functions:**
|
|
174
|
+
- `getPiProfile(req, res, dependencies, config)`
|
|
175
|
+
- `getPiAnalytics(req, res, dependencies, config)`
|
|
176
|
+
|
|
177
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `review_helpers`, `data/computation_helpers`
|
|
178
|
+
|
|
179
|
+
### 10. profile/user_profile_helpers.js
|
|
180
|
+
**Purpose:** Signed-in user profile endpoints
|
|
181
|
+
**Functions:**
|
|
182
|
+
- `getUserVerification(req, res, dependencies, config)`
|
|
183
|
+
- `checkIfUserIsPopularInvestor(req, res, dependencies, config)` - moved from data_helpers
|
|
184
|
+
|
|
185
|
+
**Dependencies:** `core/user_status_helpers`, `verification_helpers`
|
|
186
|
+
|
|
187
|
+
### 11. profile/profile_view_helpers.js
|
|
188
|
+
**Purpose:** Profile view tracking
|
|
189
|
+
**Functions:**
|
|
190
|
+
- `trackProfileView(req, res, dependencies, config)`
|
|
191
|
+
|
|
192
|
+
**Dependencies:** `@google-cloud/firestore`
|
|
193
|
+
|
|
194
|
+
### 12. watchlist/watchlist_data_helpers.js
|
|
195
|
+
**Purpose:** Legacy watchlist endpoints (for backward compatibility)
|
|
196
|
+
**Functions:**
|
|
197
|
+
- `getWatchlist(req, res, dependencies, config)` - legacy single watchlist
|
|
198
|
+
- `updateWatchlist(req, res, dependencies, config)` - legacy single watchlist
|
|
199
|
+
|
|
200
|
+
**Dependencies:** `watchlist_helpers` (new multi-watchlist system)
|
|
201
|
+
|
|
202
|
+
### 13. watchlist/watchlist_generation_helpers.js
|
|
203
|
+
**Purpose:** Watchlist auto-generation
|
|
204
|
+
**Functions:**
|
|
205
|
+
- `autoGenerateWatchlist(req, res, dependencies, config)`
|
|
206
|
+
|
|
207
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `watchlist_helpers`
|
|
208
|
+
|
|
209
|
+
### 14. watchlist/watchlist_analytics_helpers.js
|
|
210
|
+
**Purpose:** Watchlist analytics
|
|
211
|
+
**Functions:**
|
|
212
|
+
- `getWatchlistTriggerCounts(req, res, dependencies, config)`
|
|
213
|
+
|
|
214
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `watchlist_helpers`
|
|
215
|
+
|
|
216
|
+
### 15. recommendations/recommendation_helpers.js
|
|
217
|
+
**Purpose:** User recommendations
|
|
218
|
+
**Functions:**
|
|
219
|
+
- `getUserRecommendations(req, res, dependencies, config, type)`
|
|
220
|
+
|
|
221
|
+
**Dependencies:** `@google-cloud/firestore`
|
|
222
|
+
|
|
223
|
+
### 16. search/pi_search_helpers.js
|
|
224
|
+
**Purpose:** Popular Investor search
|
|
225
|
+
**Functions:**
|
|
226
|
+
- `searchPopularInvestors(req, res, dependencies, config)`
|
|
227
|
+
|
|
228
|
+
**Dependencies:** `core/data_lookup_helpers`, `@google-cloud/firestore`
|
|
229
|
+
|
|
230
|
+
### 17. search/pi_request_helpers.js
|
|
231
|
+
**Purpose:** PI addition requests and rankings checks
|
|
232
|
+
**Functions:**
|
|
233
|
+
- `requestPiAddition(req, res, dependencies, config)`
|
|
234
|
+
- `checkPisInRankings(req, res, dependencies, config)`
|
|
235
|
+
|
|
236
|
+
**Dependencies:** `core/data_lookup_helpers`, `@google-cloud/firestore`
|
|
237
|
+
|
|
238
|
+
### 18. metrics/personalized_metrics_helpers.js
|
|
239
|
+
**Purpose:** Personalized metrics for signed-in users
|
|
240
|
+
**Functions:**
|
|
241
|
+
- `getSignedInUserPIPersonalizedMetrics(req, res, dependencies, config)`
|
|
242
|
+
- `generateSamplePIPersonalizedMetrics(userCid, rankEntry)` - internal helper
|
|
243
|
+
|
|
244
|
+
**Dependencies:** `core/data_lookup_helpers`, `core/compression_helpers`, `core/user_status_helpers`, `review_helpers`
|
|
245
|
+
|
|
246
|
+
## Migration Strategy
|
|
247
|
+
|
|
248
|
+
### Phase 1: Create Core Helpers
|
|
249
|
+
1. Create `helpers/core/` directory
|
|
250
|
+
2. Extract compression utilities → `compression_helpers.js`
|
|
251
|
+
3. Extract data lookup utilities → `data_lookup_helpers.js`
|
|
252
|
+
4. Extract user status checks → `user_status_helpers.js`
|
|
253
|
+
5. Enhance path resolution → `path_resolution_helpers.js`
|
|
254
|
+
|
|
255
|
+
### Phase 2: Create Data Helpers
|
|
256
|
+
1. Create `helpers/data/` directory
|
|
257
|
+
2. Extract portfolio functions → `portfolio_helpers.js`
|
|
258
|
+
3. Extract social functions → `social_helpers.js`
|
|
259
|
+
4. Extract computation functions → `computation_helpers.js`
|
|
260
|
+
5. Extract instrument functions → `instrument_helpers.js`
|
|
261
|
+
|
|
262
|
+
### Phase 3: Create Profile Helpers
|
|
263
|
+
1. Create `helpers/profile/` directory
|
|
264
|
+
2. Extract PI profile functions → `pi_profile_helpers.js`
|
|
265
|
+
3. Extract user profile functions → `user_profile_helpers.js`
|
|
266
|
+
4. Extract profile view functions → `profile_view_helpers.js`
|
|
267
|
+
|
|
268
|
+
### Phase 4: Create Feature-Specific Helpers
|
|
269
|
+
1. Create `helpers/watchlist/` directory
|
|
270
|
+
2. Create `helpers/recommendations/` directory
|
|
271
|
+
3. Create `helpers/search/` directory
|
|
272
|
+
4. Create `helpers/metrics/` directory
|
|
273
|
+
5. Extract respective functions
|
|
274
|
+
|
|
275
|
+
### Phase 5: Update Imports
|
|
276
|
+
1. Update `index.js` to import from new locations
|
|
277
|
+
2. Update all helper files to use new core utilities
|
|
278
|
+
3. Keep `collection_helpers.js` for backward compatibility (re-export from core)
|
|
279
|
+
|
|
280
|
+
### Phase 6: Cleanup
|
|
281
|
+
1. Delete old `data_helpers.js`
|
|
282
|
+
2. Update any external references
|
|
283
|
+
3. Run tests to ensure everything works
|
|
284
|
+
|
|
285
|
+
## Benefits
|
|
286
|
+
|
|
287
|
+
1. **Modularity**: Each file has a clear, single responsibility
|
|
288
|
+
2. **Maintainability**: Easier to find and update specific functionality
|
|
289
|
+
3. **Testability**: Smaller files are easier to test
|
|
290
|
+
4. **Reusability**: Core utilities can be shared across modules
|
|
291
|
+
5. **Readability**: Clear organization makes code easier to understand
|
|
292
|
+
6. **Scalability**: Easy to add new features without bloating existing files
|
|
293
|
+
|
|
294
|
+
## File Size Targets
|
|
295
|
+
|
|
296
|
+
- **Core helpers**: 100-300 lines each
|
|
297
|
+
- **Data helpers**: 200-400 lines each
|
|
298
|
+
- **Profile helpers**: 200-500 lines each
|
|
299
|
+
- **Feature helpers**: 200-400 lines each
|
|
300
|
+
- **Total**: Better organized, easier to navigate
|
|
301
|
+
|
|
302
|
+
## Implementation Order
|
|
303
|
+
|
|
304
|
+
1. ✅ Create migration plan (done)
|
|
305
|
+
2. ⏳ Create core helpers (compression, data lookup, user status, path resolution)
|
|
306
|
+
3. ⏳ Create data helpers (portfolio, social, computation, instrument)
|
|
307
|
+
4. ⏳ Create profile helpers (PI profile, user profile, profile view)
|
|
308
|
+
5. ⏳ Create feature helpers (watchlist, recommendations, search, metrics)
|
|
309
|
+
6. ⏳ Update index.js imports
|
|
310
|
+
7. ⏳ Update all helper files to use new structure
|
|
311
|
+
8. ⏳ Test all endpoints
|
|
312
|
+
9. ⏳ Remove old data_helpers.js
|
|
313
|
+
|
|
314
|
+
## Notes
|
|
315
|
+
|
|
316
|
+
- Keep backward compatibility during migration
|
|
317
|
+
- Use re-exports where needed to avoid breaking changes
|
|
318
|
+
- Update tests as we go
|
|
319
|
+
- Document new structure in README
|
|
320
|
+
|