bulltrackers-module 1.0.31 → 1.0.32

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.
@@ -5,8 +5,50 @@
5
5
 
6
6
  const express = require('express');
7
7
  const cors = require('cors');
8
+ // FieldPath is needed to query by document ID
9
+ const { FieldPath } = require('@google-cloud/firestore');
8
10
  const { buildCalculationMap, createApiHandler } = require('./helpers/api_helpers.js');
9
11
 
12
+ /**
13
+ * Recursively creates a snippet of a data object, limiting arrays and large maps.
14
+ * @param {*} data - The data to snippet.
15
+ * @param {number} [maxKeys=20] - Max keys before an object is considered a "large map".
16
+ * @returns {*} - The snippet.
17
+ */
18
+ function createStructureSnippet(data, maxKeys = 20) {
19
+ // Handle primitives and null
20
+ if (data === null || typeof data !== 'object') {
21
+ return data;
22
+ }
23
+
24
+ // Handle arrays: return a snippet of the first element
25
+ if (Array.isArray(data)) {
26
+ if (data.length === 0) {
27
+ return "<empty array>";
28
+ }
29
+ // Recursively snippet the first item
30
+ return [ createStructureSnippet(data[0], maxKeys) ];
31
+ }
32
+
33
+ // Handle objects
34
+ const newObj = {};
35
+ const keys = Object.keys(data);
36
+
37
+ // If it's a large map (e.g., user IDs), just show one example
38
+ if (keys.length > maxKeys) {
39
+ const firstKey = keys[0];
40
+ newObj[firstKey] = createStructureSnippet(data[firstKey], maxKeys);
41
+ newObj[`... (${keys.length - 1} more keys)`] = "<object>";
42
+ } else {
43
+ // Otherwise, it's a structural object, so map all keys
44
+ for (const key of keys) {
45
+ newObj[key] = createStructureSnippet(data[key], maxKeys);
46
+ }
47
+ }
48
+ return newObj;
49
+ }
50
+
51
+
10
52
  /**
11
53
  * Creates and configures the Express app for the Generic API.
12
54
  * @param {object} config - The Generic API V2 configuration object.
@@ -19,7 +61,6 @@ function createApiApp(config, dependencies, unifiedCalculations) {
19
61
  const { firestore, logger } = dependencies; // Extract dependencies
20
62
 
21
63
  // --- Pre-compute Calculation Map ---
22
- // This doesn't need config directly
23
64
  const calcMap = buildCalculationMap(unifiedCalculations);
24
65
 
25
66
  // --- Middleware ---
@@ -27,7 +68,6 @@ function createApiApp(config, dependencies, unifiedCalculations) {
27
68
  app.use(express.json());
28
69
 
29
70
  // --- Main API Endpoint ---
30
- // The handler is created with its dependencies and config injected
31
71
  app.get('/', createApiHandler(config, firestore, logger, calcMap));
32
72
 
33
73
  // --- Health Check Endpoint ---
@@ -35,7 +75,7 @@ function createApiApp(config, dependencies, unifiedCalculations) {
35
75
  res.status(200).send('OK');
36
76
  });
37
77
 
38
- // --- NEW: Debug Endpoint to list all computation keys ---
78
+ // --- Debug Endpoint to list all computation keys ---
39
79
  app.get('/list-computations', (req, res) => {
40
80
  try {
41
81
  const computationKeys = Object.keys(calcMap);
@@ -50,38 +90,62 @@ function createApiApp(config, dependencies, unifiedCalculations) {
50
90
  }
51
91
  });
52
92
 
53
- // --- NEW: Debug Endpoint to get the structure of a computation ---
54
- app.get('/structure/:computationName', (req, res) => {
93
+ // --- REVISED: Debug Endpoint to get the structure from Firestore ---
94
+ app.get('/structure/:computationName', async (req, res) => {
55
95
  const { computationName } = req.params;
56
96
  try {
97
+ // 1. Find the computation category
57
98
  const pathInfo = calcMap[computationName];
58
99
  if (!pathInfo) {
59
- return res.status(404).send({ status: 'error', message: `Computation '${computationName}' not found.` });
100
+ return res.status(404).send({ status: 'error', message: `Computation '${computationName}' not found in calculation map.` });
60
101
  }
61
-
62
102
  const { category } = pathInfo;
63
- const CalculationClass = unifiedCalculations[category]?.[computationName];
64
103
 
65
- if (typeof CalculationClass !== 'function') {
66
- logger.log('WARN', `API /structure: No calculation class found for ${category}/${computationName}.`);
67
- return res.status(404).send({ status: 'error', message: `Could not find calculation class for '${computationName}'.` });
104
+ // 2. Get collection names from config
105
+ const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
106
+ const resultsSub = config.resultsSubcollection || 'results';
107
+ const compsSub = config.computationsSubcollection || 'computations';
108
+
109
+ // 3. Find the most recent date document in Firestore
110
+ const dateQuery = firestore.collection(insightsCollection)
111
+ .orderBy(FieldPath.documentId(), 'desc')
112
+ .limit(1);
113
+
114
+ const dateSnapshot = await dateQuery.get();
115
+
116
+ if (dateSnapshot.empty) {
117
+ return res.status(404).send({ status: 'error', message: `No data found in insights collection '${insightsCollection}'.` });
68
118
  }
119
+
120
+ const recentDate = dateSnapshot.docs[0].id;
121
+
122
+ // 4. Construct the path to the actual computation document
123
+ const docRef = firestore.collection(insightsCollection).doc(recentDate)
124
+ .collection(resultsSub).doc(category)
125
+ .collection(compsSub).doc(computationName);
126
+
127
+ const doc = await docRef.get();
69
128
 
70
- // Instantiate the class and get its default result
71
- const calcInstance = new CalculationClass();
72
- if (typeof calcInstance.getResult === 'function') {
73
- const structure = calcInstance.getResult();
74
- res.status(200).send({
75
- status: 'success',
76
- computation: computationName,
77
- category: category,
78
- structure: structure || {}, // Ensure it returns at least an empty object
129
+ if (!doc.exists) {
130
+ return res.status(404).send({
131
+ status: 'error',
132
+ message: `Data for '${computationName}' not found for the most recent date (${recentDate}).`,
133
+ checkedPath: docRef.path
79
134
  });
80
- } else {
81
- logger.log('WARN', `API /structure: Class ${category}/${computationName} has no getResult() method.`);
82
- return res.status(501).send({ status: 'error', message: `Calculation '${computationName}' exists but does not have a getResult() method to determine structure.` });
83
135
  }
84
136
 
137
+ // 5. Generate and return the snippet
138
+ const fullData = doc.data();
139
+ const snippet = createStructureSnippet(fullData);
140
+
141
+ res.status(200).send({
142
+ status: 'success',
143
+ computation: computationName,
144
+ category: category,
145
+ exampleDate: recentDate,
146
+ structureSnippet: snippet,
147
+ });
148
+
85
149
  } catch (error) {
86
150
  logger.log('ERROR', `API /structure/${computationName} failed.`, { errorMessage: error.message, stack: error.stack });
87
151
  res.status(500).send({ status: 'error', message: `An internal error occurred while processing '${computationName}'.` });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [