bulltrackers-module 1.0.768 → 1.0.769
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-v2/UserPortfolioMetrics.js +50 -0
- package/functions/computation-system-v2/computations/BehavioralAnomaly.js +557 -337
- package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
- package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
- package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
- package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
- package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
- package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
- package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
- package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
- package/functions/computation-system-v2/config/bulltrackers.config.js +30 -128
- package/functions/computation-system-v2/core-api.js +17 -9
- package/functions/computation-system-v2/data_schema_reference.MD +108 -0
- package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
- package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
- package/functions/computation-system-v2/devtools/index.js +36 -0
- package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
- package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
- package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
- package/functions/computation-system-v2/devtools/shared/index.js +16 -0
- package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
- package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
- package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
- package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
- package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
- package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
- package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
- package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
- package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
- package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
- package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
- package/functions/computation-system-v2/framework/data/DataFetcher.js +250 -184
- package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
- package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
- package/functions/computation-system-v2/framework/execution/Orchestrator.js +215 -129
- package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
- package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
- package/functions/computation-system-v2/framework/storage/StorageManager.js +105 -67
- package/functions/computation-system-v2/framework/testing/ComputationTester.js +12 -6
- package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
- package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
- package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
- package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
- package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
- package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Materialized View Manager
|
|
3
|
+
* * V2.1 UPDATE: Added verbose logging for long-running MV creation operations.
|
|
4
|
+
*/
|
|
5
|
+
class MaterializedViewManager {
|
|
6
|
+
constructor(bigqueryClient, logger, config) {
|
|
7
|
+
this.bq = bigqueryClient;
|
|
8
|
+
this.logger = logger || console;
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.checkedViews = new Set();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async ensureMetricView(baseTable, spec) {
|
|
14
|
+
const suffix = spec.jsonPath ? `_${this._hash(spec.jsonPath)}` : '';
|
|
15
|
+
const viewName = `mv_${baseTable}_${spec.func}_${spec.field}${suffix}_by_day`.toLowerCase();
|
|
16
|
+
|
|
17
|
+
if (this.checkedViews.has(viewName)) return viewName;
|
|
18
|
+
|
|
19
|
+
const dataset = this.bq.dataset(this.config.dataset);
|
|
20
|
+
const table = dataset.table(viewName);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const [exists] = await table.exists();
|
|
24
|
+
if (exists) {
|
|
25
|
+
this.checkedViews.add(viewName);
|
|
26
|
+
return viewName;
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// Ignore 404s
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await this._createView(dataset, viewName, baseTable, spec);
|
|
33
|
+
this.checkedViews.add(viewName);
|
|
34
|
+
return viewName;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async _createView(dataset, viewName, baseTable, spec) {
|
|
38
|
+
// LOGGING ADDED HERE
|
|
39
|
+
this.logger.log('INFO', `[MV-Manager] ⏳ Creating Materialized View: ${viewName}...`);
|
|
40
|
+
this.logger.log('INFO', `[MV-Manager] ⚠️ This triggers a historical backfill and may take 30-60 seconds. Please wait.`);
|
|
41
|
+
|
|
42
|
+
const { field, func, entityField, dateField, jsonPath } = spec;
|
|
43
|
+
|
|
44
|
+
let valueExpression = field;
|
|
45
|
+
if (jsonPath) {
|
|
46
|
+
valueExpression = `CAST(JSON_VALUE(${field}, '${jsonPath}') AS FLOAT64)`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const query = `
|
|
50
|
+
CREATE MATERIALIZED VIEW \`${dataset.id}.${viewName}\`
|
|
51
|
+
OPTIONS (enable_refresh = true, refresh_interval_minutes = 60)
|
|
52
|
+
AS SELECT
|
|
53
|
+
${entityField} as entity_id,
|
|
54
|
+
${dateField} as date,
|
|
55
|
+
${func}(${valueExpression}) as value,
|
|
56
|
+
COUNT(*) as count
|
|
57
|
+
FROM \`${dataset.id}.${baseTable}\`
|
|
58
|
+
GROUP BY 1, 2
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await dataset.query({ query });
|
|
63
|
+
this.logger.log('INFO', `[MV-Manager] ✅ Successfully created ${viewName}. Backfill running in background.`);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
if (e.message.includes('Already Exists')) {
|
|
66
|
+
this.logger.log('INFO', `[MV-Manager] View ${viewName} was created concurrently.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`Failed to create Materialized View ${viewName}: ${e.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_hash(str) {
|
|
74
|
+
let hash = 0;
|
|
75
|
+
for (let i = 0; i < str.length; i++) {
|
|
76
|
+
const char = str.charCodeAt(i);
|
|
77
|
+
hash = (hash << 5) - hash + char;
|
|
78
|
+
hash |= 0;
|
|
79
|
+
}
|
|
80
|
+
return Math.abs(hash).toString(16);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { MaterializedViewManager };
|
|
@@ -1,44 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Query Builder - Builds validated SQL queries
|
|
3
|
-
*
|
|
4
|
-
* All queries are validated against the SchemaRegistry BEFORE execution.
|
|
3
|
+
* * All queries are validated against the SchemaRegistry BEFORE execution.
|
|
5
4
|
* This catches errors early and prevents runtime failures.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} QuerySpec
|
|
10
|
-
* @property {string} table - Table name
|
|
11
|
-
* @property {string[]} [select] - Columns to select (null = *)
|
|
12
|
-
* @property {Object} [where] - WHERE conditions as { column: value }
|
|
13
|
-
* @property {string} [dateField] - Date field name for date filtering
|
|
14
|
-
* @property {string} [targetDate] - Target date (YYYY-MM-DD)
|
|
15
|
-
* @property {number} [lookback=0] - Days to look back (0 = target date only)
|
|
16
|
-
* @property {string} [entityField] - Entity field name for entity filtering
|
|
17
|
-
* @property {string[]} [entities] - Entity IDs to filter
|
|
18
|
-
* @property {string} [orderBy] - Column to order by
|
|
19
|
-
* @property {string} [orderDir='DESC'] - Order direction
|
|
20
|
-
* @property {number} [limit] - Limit number of rows
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @typedef {Object} BuiltQuery
|
|
25
|
-
* @property {string} sql - The SQL query string
|
|
26
|
-
* @property {Object} params - Query parameters
|
|
27
|
-
* @property {string} table - Table that was queried
|
|
28
|
-
* @property {Object} spec - Original query spec
|
|
5
|
+
* * V2.0 UPDATE: Safety features (Ban SELECT * and enforce partitions).
|
|
29
6
|
*/
|
|
30
7
|
|
|
31
8
|
class QueryBuilder {
|
|
32
9
|
/**
|
|
33
10
|
* @param {Object} config - Configuration object
|
|
34
|
-
* @param {string} config.projectId - GCP project ID
|
|
35
|
-
* @param {string} config.dataset - BigQuery dataset name
|
|
36
11
|
* @param {SchemaRegistry} schemaRegistry - Schema registry instance
|
|
37
12
|
* @param {Object} [logger] - Logger instance
|
|
38
13
|
*/
|
|
39
14
|
constructor(config, schemaRegistry, logger = null) {
|
|
40
15
|
this.projectId = config.projectId;
|
|
41
16
|
this.dataset = config.dataset;
|
|
17
|
+
this.tables = config.tables || {}; // Store table config for validation
|
|
42
18
|
this.schemaRegistry = schemaRegistry;
|
|
43
19
|
this.logger = logger;
|
|
44
20
|
}
|
|
@@ -47,7 +23,7 @@ class QueryBuilder {
|
|
|
47
23
|
* Build a validated query.
|
|
48
24
|
* @param {QuerySpec} spec - Query specification
|
|
49
25
|
* @returns {Promise<BuiltQuery>}
|
|
50
|
-
* @throws {Error} If validation fails
|
|
26
|
+
* @throws {Error} If validation fails or safety checks trigger
|
|
51
27
|
*/
|
|
52
28
|
async build(spec) {
|
|
53
29
|
const { table } = spec;
|
|
@@ -56,6 +32,27 @@ class QueryBuilder {
|
|
|
56
32
|
throw new Error('[QueryBuilder] Table name is required');
|
|
57
33
|
}
|
|
58
34
|
|
|
35
|
+
// --- 1. PARTITION SAFETY CHECK ---
|
|
36
|
+
// If the table is configured with a Date Field (Partition),
|
|
37
|
+
// we MUST see a targetDate or a custom where-clause using that field.
|
|
38
|
+
// Otherwise, it's a guaranteed full-table scan over all history.
|
|
39
|
+
const tableConfig = this.tables[table];
|
|
40
|
+
|
|
41
|
+
if (tableConfig && tableConfig.dateField) {
|
|
42
|
+
const hasTargetDate = !!spec.targetDate;
|
|
43
|
+
// Check if 'where' clause contains the date field key
|
|
44
|
+
const hasCustomDateFilter = spec.where && Object.keys(spec.where).includes(tableConfig.dateField);
|
|
45
|
+
|
|
46
|
+
if (!hasTargetDate && !hasCustomDateFilter) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`[QueryBuilder] 🛑 UNSAFE QUERY BLOCKED: Table '${table}' is partitioned by '${tableConfig.dateField}', ` +
|
|
49
|
+
`but no date filter was provided. This would trigger a full table scan. ` +
|
|
50
|
+
`Please provide a 'lookback' or explicit filter in your computation config.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------
|
|
55
|
+
|
|
59
56
|
// Validate table exists and get schema
|
|
60
57
|
const schema = await this.schemaRegistry.getSchema(table);
|
|
61
58
|
|
|
@@ -99,13 +96,8 @@ class QueryBuilder {
|
|
|
99
96
|
|
|
100
97
|
/**
|
|
101
98
|
* Build a simple existence check query.
|
|
102
|
-
* @param {string} table - Table name
|
|
103
|
-
* @param {string} dateField - Date field name
|
|
104
|
-
* @param {string} targetDate - Target date
|
|
105
|
-
* @returns {Promise<BuiltQuery>}
|
|
106
99
|
*/
|
|
107
100
|
async buildExistsQuery(table, dateField, targetDate) {
|
|
108
|
-
// Validate table and date field
|
|
109
101
|
await this.schemaRegistry.getSchema(table);
|
|
110
102
|
if (dateField) {
|
|
111
103
|
await this.schemaRegistry.validateColumns(table, [dateField]);
|
|
@@ -148,10 +140,18 @@ class QueryBuilder {
|
|
|
148
140
|
const params = {};
|
|
149
141
|
const conditions = [];
|
|
150
142
|
|
|
151
|
-
// SELECT
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
// --- 2. SELECT * SAFETY CHECK ---
|
|
144
|
+
// BigQuery charges by column. SELECT * on a 100-col table costs 100x.
|
|
145
|
+
// We force developers to be specific.
|
|
146
|
+
if (!select || select.length === 0 || select.includes('*')) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`[QueryBuilder] 🛑 LAZY SELECT BLOCKED: 'SELECT *' or empty select is forbidden to save costs. ` +
|
|
149
|
+
`Table: ${table}. Please list the specific columns you need in the 'fields' array of your computation config.`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const selectClause = select.join(', ');
|
|
154
|
+
// --------------------------------
|
|
155
155
|
|
|
156
156
|
// Date filtering
|
|
157
157
|
if (dateField && targetDate) {
|
|
@@ -229,4 +229,4 @@ class QueryBuilder {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
module.exports = { QueryBuilder };
|
|
232
|
+
module.exports = { QueryBuilder };
|