@objectstack/service-analytics 7.9.0 → 8.0.0
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/dist/index.cjs +578 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -7
- package/dist/index.d.ts +165 -7
- package/dist/index.js +570 -18
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { IAnalyticsService, Logger, DriverCapabilities, AnalyticsStrategy, AnalyticsQuery, AnalyticsResult, CubeMeta, StrategyContext } from '@objectstack/spec/contracts';
|
|
2
|
-
export { AnalyticsStrategy, DriverCapabilities, StrategyContext } from '@objectstack/spec/contracts';
|
|
3
|
-
import { Cube } from '@objectstack/spec/data';
|
|
1
|
+
import { IAnalyticsService, Logger, DriverCapabilities, AnalyticsStrategy, AnalyticsQuery, AnalyticsResult, DatasetSelection, CubeMeta, DatasetCompareTo, StrategyContext } from '@objectstack/spec/contracts';
|
|
2
|
+
export { AnalyticsStrategy, DatasetSelection, DriverCapabilities, StrategyContext } from '@objectstack/spec/contracts';
|
|
3
|
+
import { Cube, FilterCondition } from '@objectstack/spec/data';
|
|
4
|
+
import { ExecutionContext } from '@objectstack/spec/kernel';
|
|
5
|
+
import { Dataset } from '@objectstack/spec/ui';
|
|
4
6
|
import { Plugin, PluginContext } from '@objectstack/core';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -52,6 +54,35 @@ declare class CubeRegistry {
|
|
|
52
54
|
private fieldTypeToDimensionType;
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
interface DerivedMeasureSpec {
|
|
58
|
+
name: string;
|
|
59
|
+
op: 'ratio' | 'sum' | 'difference' | 'product';
|
|
60
|
+
of: string[];
|
|
61
|
+
}
|
|
62
|
+
interface CompiledDataset {
|
|
63
|
+
/** The Cube the dataset compiles to (consumed by the strategy chain). */
|
|
64
|
+
cube: Cube;
|
|
65
|
+
/**
|
|
66
|
+
* Relationship names declared in `include`. The join allowlist (D-C):
|
|
67
|
+
* the NativeSQLStrategy rejects any join alias not in this set.
|
|
68
|
+
*/
|
|
69
|
+
allowedRelationships: Set<string>;
|
|
70
|
+
/** Derived measures, computed post-aggregation by the executor (Q1). */
|
|
71
|
+
derived: DerivedMeasureSpec[];
|
|
72
|
+
/** Definition-level filter (the dataset's intrinsic scope). */
|
|
73
|
+
filter?: FilterCondition;
|
|
74
|
+
/** Per-measure scoped filters, keyed by measure name (applied by executor). */
|
|
75
|
+
measureFilters: Record<string, FilterCondition>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolves a relationship name on a base object to the related object/table
|
|
79
|
+
* name, using the runtime's object graph. Optional: when omitted the compiler
|
|
80
|
+
* trusts the declared `include` names (the NativeSQLStrategy convention assumes
|
|
81
|
+
* the relationship name equals the related table name).
|
|
82
|
+
*/
|
|
83
|
+
type RelationshipResolver = (baseObject: string, relationshipName: string) => string | undefined;
|
|
84
|
+
declare function compileDataset(dataset: Dataset, resolver?: RelationshipResolver): CompiledDataset;
|
|
85
|
+
|
|
55
86
|
/**
|
|
56
87
|
* Configuration for AnalyticsService.
|
|
57
88
|
*/
|
|
@@ -93,6 +124,30 @@ interface AnalyticsServiceConfig {
|
|
|
93
124
|
* They are merged with the built-in strategies and sorted by priority.
|
|
94
125
|
*/
|
|
95
126
|
strategies?: AnalyticsStrategy[];
|
|
127
|
+
/**
|
|
128
|
+
* ADR-0021 D-C — context-aware per-object read scope (tenant + RLS). Supplied
|
|
129
|
+
* by the runtime that owns the sharing middleware; receives the current
|
|
130
|
+
* request's ExecutionContext and returns the RLS `FilterCondition` for the
|
|
131
|
+
* object (exactly what `RLSCompiler` emits). The service binds the active
|
|
132
|
+
* context per query and the strategy compiles the filter into alias-qualified
|
|
133
|
+
* SQL injected into every base and joined table.
|
|
134
|
+
*/
|
|
135
|
+
getReadScope?: (objectName: string, context?: ExecutionContext) => FilterCondition | null | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* ADR-0021 D-C — join allowlist per cube (the dataset's declared `include`).
|
|
138
|
+
* Joins outside this set are rejected by the strategy. Compiled datasets
|
|
139
|
+
* (via `queryDataset`/`registerDataset`) supply this automatically; this
|
|
140
|
+
* config hook is a fallback for legacy hand-authored cubes.
|
|
141
|
+
*/
|
|
142
|
+
getAllowedRelationships?: (cubeName: string) => Set<string> | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* ADR-0021 — optional object-graph resolver used when compiling datasets:
|
|
145
|
+
* `(baseObject, relationshipName) => relatedObjectName | undefined`. When
|
|
146
|
+
* provided, `queryDataset` validates that every declared `include` exists.
|
|
147
|
+
*/
|
|
148
|
+
relationshipResolver?: RelationshipResolver;
|
|
149
|
+
/** Pre-defined datasets to compile + register at construction (ADR-0021). */
|
|
150
|
+
datasets?: Dataset[];
|
|
96
151
|
}
|
|
97
152
|
/**
|
|
98
153
|
* AnalyticsService — Multi-driver analytics orchestrator.
|
|
@@ -114,14 +169,40 @@ interface AnalyticsServiceConfig {
|
|
|
114
169
|
*/
|
|
115
170
|
declare class AnalyticsService implements IAnalyticsService {
|
|
116
171
|
private readonly strategies;
|
|
117
|
-
|
|
172
|
+
/** Context-independent part of the StrategyContext (no per-request scope). */
|
|
173
|
+
private readonly baseCtx;
|
|
174
|
+
/** Context-aware read-scope provider (bound to the request's context per call). */
|
|
175
|
+
private readonly readScopeProvider?;
|
|
176
|
+
/** Compiled datasets by name — feeds the join allowlist (D-C) and queryDataset. */
|
|
177
|
+
private readonly datasetRegistry;
|
|
178
|
+
/** Optional object-graph resolver used when compiling datasets. */
|
|
179
|
+
private readonly relationshipResolver?;
|
|
118
180
|
readonly cubeRegistry: CubeRegistry;
|
|
119
181
|
private readonly logger;
|
|
120
182
|
constructor(config?: AnalyticsServiceConfig);
|
|
183
|
+
/**
|
|
184
|
+
* Build a per-call StrategyContext that binds the read-scope provider to the
|
|
185
|
+
* current request's ExecutionContext (ADR-0021 D-C). The strategy then sees a
|
|
186
|
+
* `getReadScope(objectName)` that already knows the active tenant.
|
|
187
|
+
*/
|
|
188
|
+
private callCtx;
|
|
121
189
|
/**
|
|
122
190
|
* Execute an analytical query by delegating to the first capable strategy.
|
|
123
191
|
*/
|
|
124
|
-
query(query: AnalyticsQuery): Promise<AnalyticsResult>;
|
|
192
|
+
query(query: AnalyticsQuery, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
193
|
+
/**
|
|
194
|
+
* Compile a `dataset` (ADR-0021) and register its Cube + join allowlist so it
|
|
195
|
+
* can be queried by name. Idempotent (re-registering overwrites). Returns the
|
|
196
|
+
* compiled dataset.
|
|
197
|
+
*/
|
|
198
|
+
registerDataset(dataset: Dataset): CompiledDataset;
|
|
199
|
+
/**
|
|
200
|
+
* Execute a semantic-layer dataset (ADR-0021). Compiles the dataset (saved or
|
|
201
|
+
* inline draft — Studio preview), registers its Cube + join allowlist, then
|
|
202
|
+
* runs the selection through the `DatasetExecutor` with the request context so
|
|
203
|
+
* tenant/RLS scoping (D-C) is applied. See {@link IAnalyticsService.queryDataset}.
|
|
204
|
+
*/
|
|
205
|
+
queryDataset(dataset: Dataset, selection: DatasetSelection, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
125
206
|
/**
|
|
126
207
|
* Get cube metadata for discovery.
|
|
127
208
|
*/
|
|
@@ -129,7 +210,7 @@ declare class AnalyticsService implements IAnalyticsService {
|
|
|
129
210
|
/**
|
|
130
211
|
* Generate SQL for a query without executing it (dry-run).
|
|
131
212
|
*/
|
|
132
|
-
generateSql(query: AnalyticsQuery): Promise<{
|
|
213
|
+
generateSql(query: AnalyticsQuery, context?: ExecutionContext): Promise<{
|
|
133
214
|
sql: string;
|
|
134
215
|
params: unknown[];
|
|
135
216
|
}>;
|
|
@@ -181,6 +262,20 @@ interface AnalyticsServicePluginOptions {
|
|
|
181
262
|
}>;
|
|
182
263
|
filter?: Record<string, unknown>;
|
|
183
264
|
}) => Promise<Record<string, unknown>[]>;
|
|
265
|
+
/**
|
|
266
|
+
* ADR-0021 D-C — context-aware per-object read scope (tenant + RLS). The
|
|
267
|
+
* runtime supplies this from its sharing middleware so the analytics raw-SQL
|
|
268
|
+
* path cannot bypass tenant isolation. Receives the request's ExecutionContext
|
|
269
|
+
* and returns the RLS `FilterCondition` for the object (what `RLSCompiler`
|
|
270
|
+
* emits). When omitted, the plugin auto-bridges to a registered `'security'`
|
|
271
|
+
* service exposing `getReadFilter(object, context)` if one is present.
|
|
272
|
+
*/
|
|
273
|
+
getReadScope?: (objectName: string, context?: ExecutionContext) => FilterCondition | null | undefined;
|
|
274
|
+
/**
|
|
275
|
+
* ADR-0021 D-C — join allowlist per cube (the dataset's declared `include`).
|
|
276
|
+
* Typically wired from the dataset registry's compiled `allowedRelationships`.
|
|
277
|
+
*/
|
|
278
|
+
getAllowedRelationships?: (cubeName: string) => Set<string> | undefined;
|
|
184
279
|
/** Enable debug logging. */
|
|
185
280
|
debug?: boolean;
|
|
186
281
|
}
|
|
@@ -225,6 +320,59 @@ declare class AnalyticsServicePlugin implements Plugin {
|
|
|
225
320
|
destroy(): Promise<void>;
|
|
226
321
|
}
|
|
227
322
|
|
|
323
|
+
/** @deprecated use DatasetCompareTo from @objectstack/spec/contracts */
|
|
324
|
+
type CompareTo = DatasetCompareTo;
|
|
325
|
+
/**
|
|
326
|
+
* Dataset executor (ADR-0021 WS2).
|
|
327
|
+
*
|
|
328
|
+
* Turns a compiled dataset + a presentation's selection (dimensions, measures,
|
|
329
|
+
* runtime filter, compareTo) into one or more `AnalyticsQuery`s against the Cube
|
|
330
|
+
* runtime, then post-processes the results:
|
|
331
|
+
* - resolves the base measures a selection needs (including derived deps),
|
|
332
|
+
* - applies measure-scoped filters via supplementary grouped queries,
|
|
333
|
+
* - evaluates derived measures (ratio/sum/difference/product) row-by-row (Q1),
|
|
334
|
+
* - shifts the query for `compareTo` (previousPeriod / previousYear) and
|
|
335
|
+
* attaches `<measure>__compare` columns.
|
|
336
|
+
*
|
|
337
|
+
* RLS/tenant scoping is NOT handled here — it is enforced inside the strategy
|
|
338
|
+
* via the StrategyContext read-scope hook (D-C). This layer is pure query
|
|
339
|
+
* shaping + arithmetic.
|
|
340
|
+
*/
|
|
341
|
+
/** AND two optional FilterConditions into one (MongoDB-style). */
|
|
342
|
+
declare function combineFilters(a?: FilterCondition, b?: FilterCondition): FilterCondition | undefined;
|
|
343
|
+
/**
|
|
344
|
+
* Evaluate derived measures on each aggregated row, mutating a shallow copy.
|
|
345
|
+
* Division by zero (and missing operands) yields `null` rather than Infinity/NaN.
|
|
346
|
+
*/
|
|
347
|
+
declare function evaluateDerivedMeasures(rows: Record<string, unknown>[], derived: DerivedMeasureSpec[]): Record<string, unknown>[];
|
|
348
|
+
/** Compute the comparison window for a [start,end] range. */
|
|
349
|
+
declare function shiftRange(range: [string, string], kind: CompareTo['kind']): [string, string];
|
|
350
|
+
declare class DatasetExecutor {
|
|
351
|
+
private readonly service;
|
|
352
|
+
constructor(service: IAnalyticsService);
|
|
353
|
+
/**
|
|
354
|
+
* Execute a dataset selection and return the shaped rows (+ field metadata).
|
|
355
|
+
*
|
|
356
|
+
* @param context - The request's ExecutionContext, threaded into every
|
|
357
|
+
* underlying `IAnalyticsService.query` so the tenant/RLS read scope is
|
|
358
|
+
* applied per request (ADR-0021 D-C).
|
|
359
|
+
*/
|
|
360
|
+
execute(compiled: CompiledDataset, selection: DatasetSelection, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
361
|
+
private buildQuery;
|
|
362
|
+
private runCompare;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Left-merge `extra` rows onto `base` rows by their dimension-key tuple,
|
|
366
|
+
* copying the listed value columns. Rows in `extra` with no base match are
|
|
367
|
+
* appended (outer-ish merge so comparison-only buckets still surface).
|
|
368
|
+
*/
|
|
369
|
+
declare function mergeByDimensions(base: Record<string, unknown>[], extra: Record<string, unknown>[], dimensions: string[], valueColumns: string[]): Record<string, unknown>[];
|
|
370
|
+
|
|
371
|
+
declare function compileScopedFilterToSql(filter: FilterCondition, alias: string): {
|
|
372
|
+
sql: string;
|
|
373
|
+
params: unknown[];
|
|
374
|
+
};
|
|
375
|
+
|
|
228
376
|
/**
|
|
229
377
|
* NativeSQLStrategy — Priority 1
|
|
230
378
|
*
|
|
@@ -241,6 +389,16 @@ declare class NativeSQLStrategy implements AnalyticsStrategy {
|
|
|
241
389
|
sql: string;
|
|
242
390
|
params: unknown[];
|
|
243
391
|
}>;
|
|
392
|
+
/**
|
|
393
|
+
* ADR-0021 D-C — inject an object's read scope (tenant + RLS predicate) into
|
|
394
|
+
* the WHERE clause. The scope is a canonical `FilterCondition` (what the
|
|
395
|
+
* RLSCompiler emits); `compileScopedFilterToSql` turns it into alias-qualified,
|
|
396
|
+
* parameterized SQL (fail-closed — it throws rather than drop a predicate).
|
|
397
|
+
* The `?` placeholders are then renumbered into the strategy's `$N` scheme.
|
|
398
|
+
* No-op when the runtime provides no scope hook (the caller is then
|
|
399
|
+
* responsible for isolation — see contract note).
|
|
400
|
+
*/
|
|
401
|
+
private applyReadScope;
|
|
244
402
|
/**
|
|
245
403
|
* Resolve a dimension/measure/filter SQL expression that may reference a
|
|
246
404
|
* related table via dot notation (e.g. `account.industry`).
|
|
@@ -318,4 +476,4 @@ declare class ObjectQLStrategy implements AnalyticsStrategy {
|
|
|
318
476
|
private buildFieldMeta;
|
|
319
477
|
}
|
|
320
478
|
|
|
321
|
-
export { AnalyticsService, type AnalyticsServiceConfig, AnalyticsServicePlugin, type AnalyticsServicePluginOptions, CubeRegistry, NativeSQLStrategy, ObjectQLStrategy };
|
|
479
|
+
export { AnalyticsService, type AnalyticsServiceConfig, AnalyticsServicePlugin, type AnalyticsServicePluginOptions, type CompareTo, type CompiledDataset, CubeRegistry, DatasetExecutor, type DerivedMeasureSpec, NativeSQLStrategy, ObjectQLStrategy, type RelationshipResolver, combineFilters, compileDataset, compileScopedFilterToSql, evaluateDerivedMeasures, mergeByDimensions, shiftRange };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { IAnalyticsService, Logger, DriverCapabilities, AnalyticsStrategy, AnalyticsQuery, AnalyticsResult, CubeMeta, StrategyContext } from '@objectstack/spec/contracts';
|
|
2
|
-
export { AnalyticsStrategy, DriverCapabilities, StrategyContext } from '@objectstack/spec/contracts';
|
|
3
|
-
import { Cube } from '@objectstack/spec/data';
|
|
1
|
+
import { IAnalyticsService, Logger, DriverCapabilities, AnalyticsStrategy, AnalyticsQuery, AnalyticsResult, DatasetSelection, CubeMeta, DatasetCompareTo, StrategyContext } from '@objectstack/spec/contracts';
|
|
2
|
+
export { AnalyticsStrategy, DatasetSelection, DriverCapabilities, StrategyContext } from '@objectstack/spec/contracts';
|
|
3
|
+
import { Cube, FilterCondition } from '@objectstack/spec/data';
|
|
4
|
+
import { ExecutionContext } from '@objectstack/spec/kernel';
|
|
5
|
+
import { Dataset } from '@objectstack/spec/ui';
|
|
4
6
|
import { Plugin, PluginContext } from '@objectstack/core';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -52,6 +54,35 @@ declare class CubeRegistry {
|
|
|
52
54
|
private fieldTypeToDimensionType;
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
interface DerivedMeasureSpec {
|
|
58
|
+
name: string;
|
|
59
|
+
op: 'ratio' | 'sum' | 'difference' | 'product';
|
|
60
|
+
of: string[];
|
|
61
|
+
}
|
|
62
|
+
interface CompiledDataset {
|
|
63
|
+
/** The Cube the dataset compiles to (consumed by the strategy chain). */
|
|
64
|
+
cube: Cube;
|
|
65
|
+
/**
|
|
66
|
+
* Relationship names declared in `include`. The join allowlist (D-C):
|
|
67
|
+
* the NativeSQLStrategy rejects any join alias not in this set.
|
|
68
|
+
*/
|
|
69
|
+
allowedRelationships: Set<string>;
|
|
70
|
+
/** Derived measures, computed post-aggregation by the executor (Q1). */
|
|
71
|
+
derived: DerivedMeasureSpec[];
|
|
72
|
+
/** Definition-level filter (the dataset's intrinsic scope). */
|
|
73
|
+
filter?: FilterCondition;
|
|
74
|
+
/** Per-measure scoped filters, keyed by measure name (applied by executor). */
|
|
75
|
+
measureFilters: Record<string, FilterCondition>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolves a relationship name on a base object to the related object/table
|
|
79
|
+
* name, using the runtime's object graph. Optional: when omitted the compiler
|
|
80
|
+
* trusts the declared `include` names (the NativeSQLStrategy convention assumes
|
|
81
|
+
* the relationship name equals the related table name).
|
|
82
|
+
*/
|
|
83
|
+
type RelationshipResolver = (baseObject: string, relationshipName: string) => string | undefined;
|
|
84
|
+
declare function compileDataset(dataset: Dataset, resolver?: RelationshipResolver): CompiledDataset;
|
|
85
|
+
|
|
55
86
|
/**
|
|
56
87
|
* Configuration for AnalyticsService.
|
|
57
88
|
*/
|
|
@@ -93,6 +124,30 @@ interface AnalyticsServiceConfig {
|
|
|
93
124
|
* They are merged with the built-in strategies and sorted by priority.
|
|
94
125
|
*/
|
|
95
126
|
strategies?: AnalyticsStrategy[];
|
|
127
|
+
/**
|
|
128
|
+
* ADR-0021 D-C — context-aware per-object read scope (tenant + RLS). Supplied
|
|
129
|
+
* by the runtime that owns the sharing middleware; receives the current
|
|
130
|
+
* request's ExecutionContext and returns the RLS `FilterCondition` for the
|
|
131
|
+
* object (exactly what `RLSCompiler` emits). The service binds the active
|
|
132
|
+
* context per query and the strategy compiles the filter into alias-qualified
|
|
133
|
+
* SQL injected into every base and joined table.
|
|
134
|
+
*/
|
|
135
|
+
getReadScope?: (objectName: string, context?: ExecutionContext) => FilterCondition | null | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* ADR-0021 D-C — join allowlist per cube (the dataset's declared `include`).
|
|
138
|
+
* Joins outside this set are rejected by the strategy. Compiled datasets
|
|
139
|
+
* (via `queryDataset`/`registerDataset`) supply this automatically; this
|
|
140
|
+
* config hook is a fallback for legacy hand-authored cubes.
|
|
141
|
+
*/
|
|
142
|
+
getAllowedRelationships?: (cubeName: string) => Set<string> | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* ADR-0021 — optional object-graph resolver used when compiling datasets:
|
|
145
|
+
* `(baseObject, relationshipName) => relatedObjectName | undefined`. When
|
|
146
|
+
* provided, `queryDataset` validates that every declared `include` exists.
|
|
147
|
+
*/
|
|
148
|
+
relationshipResolver?: RelationshipResolver;
|
|
149
|
+
/** Pre-defined datasets to compile + register at construction (ADR-0021). */
|
|
150
|
+
datasets?: Dataset[];
|
|
96
151
|
}
|
|
97
152
|
/**
|
|
98
153
|
* AnalyticsService — Multi-driver analytics orchestrator.
|
|
@@ -114,14 +169,40 @@ interface AnalyticsServiceConfig {
|
|
|
114
169
|
*/
|
|
115
170
|
declare class AnalyticsService implements IAnalyticsService {
|
|
116
171
|
private readonly strategies;
|
|
117
|
-
|
|
172
|
+
/** Context-independent part of the StrategyContext (no per-request scope). */
|
|
173
|
+
private readonly baseCtx;
|
|
174
|
+
/** Context-aware read-scope provider (bound to the request's context per call). */
|
|
175
|
+
private readonly readScopeProvider?;
|
|
176
|
+
/** Compiled datasets by name — feeds the join allowlist (D-C) and queryDataset. */
|
|
177
|
+
private readonly datasetRegistry;
|
|
178
|
+
/** Optional object-graph resolver used when compiling datasets. */
|
|
179
|
+
private readonly relationshipResolver?;
|
|
118
180
|
readonly cubeRegistry: CubeRegistry;
|
|
119
181
|
private readonly logger;
|
|
120
182
|
constructor(config?: AnalyticsServiceConfig);
|
|
183
|
+
/**
|
|
184
|
+
* Build a per-call StrategyContext that binds the read-scope provider to the
|
|
185
|
+
* current request's ExecutionContext (ADR-0021 D-C). The strategy then sees a
|
|
186
|
+
* `getReadScope(objectName)` that already knows the active tenant.
|
|
187
|
+
*/
|
|
188
|
+
private callCtx;
|
|
121
189
|
/**
|
|
122
190
|
* Execute an analytical query by delegating to the first capable strategy.
|
|
123
191
|
*/
|
|
124
|
-
query(query: AnalyticsQuery): Promise<AnalyticsResult>;
|
|
192
|
+
query(query: AnalyticsQuery, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
193
|
+
/**
|
|
194
|
+
* Compile a `dataset` (ADR-0021) and register its Cube + join allowlist so it
|
|
195
|
+
* can be queried by name. Idempotent (re-registering overwrites). Returns the
|
|
196
|
+
* compiled dataset.
|
|
197
|
+
*/
|
|
198
|
+
registerDataset(dataset: Dataset): CompiledDataset;
|
|
199
|
+
/**
|
|
200
|
+
* Execute a semantic-layer dataset (ADR-0021). Compiles the dataset (saved or
|
|
201
|
+
* inline draft — Studio preview), registers its Cube + join allowlist, then
|
|
202
|
+
* runs the selection through the `DatasetExecutor` with the request context so
|
|
203
|
+
* tenant/RLS scoping (D-C) is applied. See {@link IAnalyticsService.queryDataset}.
|
|
204
|
+
*/
|
|
205
|
+
queryDataset(dataset: Dataset, selection: DatasetSelection, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
125
206
|
/**
|
|
126
207
|
* Get cube metadata for discovery.
|
|
127
208
|
*/
|
|
@@ -129,7 +210,7 @@ declare class AnalyticsService implements IAnalyticsService {
|
|
|
129
210
|
/**
|
|
130
211
|
* Generate SQL for a query without executing it (dry-run).
|
|
131
212
|
*/
|
|
132
|
-
generateSql(query: AnalyticsQuery): Promise<{
|
|
213
|
+
generateSql(query: AnalyticsQuery, context?: ExecutionContext): Promise<{
|
|
133
214
|
sql: string;
|
|
134
215
|
params: unknown[];
|
|
135
216
|
}>;
|
|
@@ -181,6 +262,20 @@ interface AnalyticsServicePluginOptions {
|
|
|
181
262
|
}>;
|
|
182
263
|
filter?: Record<string, unknown>;
|
|
183
264
|
}) => Promise<Record<string, unknown>[]>;
|
|
265
|
+
/**
|
|
266
|
+
* ADR-0021 D-C — context-aware per-object read scope (tenant + RLS). The
|
|
267
|
+
* runtime supplies this from its sharing middleware so the analytics raw-SQL
|
|
268
|
+
* path cannot bypass tenant isolation. Receives the request's ExecutionContext
|
|
269
|
+
* and returns the RLS `FilterCondition` for the object (what `RLSCompiler`
|
|
270
|
+
* emits). When omitted, the plugin auto-bridges to a registered `'security'`
|
|
271
|
+
* service exposing `getReadFilter(object, context)` if one is present.
|
|
272
|
+
*/
|
|
273
|
+
getReadScope?: (objectName: string, context?: ExecutionContext) => FilterCondition | null | undefined;
|
|
274
|
+
/**
|
|
275
|
+
* ADR-0021 D-C — join allowlist per cube (the dataset's declared `include`).
|
|
276
|
+
* Typically wired from the dataset registry's compiled `allowedRelationships`.
|
|
277
|
+
*/
|
|
278
|
+
getAllowedRelationships?: (cubeName: string) => Set<string> | undefined;
|
|
184
279
|
/** Enable debug logging. */
|
|
185
280
|
debug?: boolean;
|
|
186
281
|
}
|
|
@@ -225,6 +320,59 @@ declare class AnalyticsServicePlugin implements Plugin {
|
|
|
225
320
|
destroy(): Promise<void>;
|
|
226
321
|
}
|
|
227
322
|
|
|
323
|
+
/** @deprecated use DatasetCompareTo from @objectstack/spec/contracts */
|
|
324
|
+
type CompareTo = DatasetCompareTo;
|
|
325
|
+
/**
|
|
326
|
+
* Dataset executor (ADR-0021 WS2).
|
|
327
|
+
*
|
|
328
|
+
* Turns a compiled dataset + a presentation's selection (dimensions, measures,
|
|
329
|
+
* runtime filter, compareTo) into one or more `AnalyticsQuery`s against the Cube
|
|
330
|
+
* runtime, then post-processes the results:
|
|
331
|
+
* - resolves the base measures a selection needs (including derived deps),
|
|
332
|
+
* - applies measure-scoped filters via supplementary grouped queries,
|
|
333
|
+
* - evaluates derived measures (ratio/sum/difference/product) row-by-row (Q1),
|
|
334
|
+
* - shifts the query for `compareTo` (previousPeriod / previousYear) and
|
|
335
|
+
* attaches `<measure>__compare` columns.
|
|
336
|
+
*
|
|
337
|
+
* RLS/tenant scoping is NOT handled here — it is enforced inside the strategy
|
|
338
|
+
* via the StrategyContext read-scope hook (D-C). This layer is pure query
|
|
339
|
+
* shaping + arithmetic.
|
|
340
|
+
*/
|
|
341
|
+
/** AND two optional FilterConditions into one (MongoDB-style). */
|
|
342
|
+
declare function combineFilters(a?: FilterCondition, b?: FilterCondition): FilterCondition | undefined;
|
|
343
|
+
/**
|
|
344
|
+
* Evaluate derived measures on each aggregated row, mutating a shallow copy.
|
|
345
|
+
* Division by zero (and missing operands) yields `null` rather than Infinity/NaN.
|
|
346
|
+
*/
|
|
347
|
+
declare function evaluateDerivedMeasures(rows: Record<string, unknown>[], derived: DerivedMeasureSpec[]): Record<string, unknown>[];
|
|
348
|
+
/** Compute the comparison window for a [start,end] range. */
|
|
349
|
+
declare function shiftRange(range: [string, string], kind: CompareTo['kind']): [string, string];
|
|
350
|
+
declare class DatasetExecutor {
|
|
351
|
+
private readonly service;
|
|
352
|
+
constructor(service: IAnalyticsService);
|
|
353
|
+
/**
|
|
354
|
+
* Execute a dataset selection and return the shaped rows (+ field metadata).
|
|
355
|
+
*
|
|
356
|
+
* @param context - The request's ExecutionContext, threaded into every
|
|
357
|
+
* underlying `IAnalyticsService.query` so the tenant/RLS read scope is
|
|
358
|
+
* applied per request (ADR-0021 D-C).
|
|
359
|
+
*/
|
|
360
|
+
execute(compiled: CompiledDataset, selection: DatasetSelection, context?: ExecutionContext): Promise<AnalyticsResult>;
|
|
361
|
+
private buildQuery;
|
|
362
|
+
private runCompare;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Left-merge `extra` rows onto `base` rows by their dimension-key tuple,
|
|
366
|
+
* copying the listed value columns. Rows in `extra` with no base match are
|
|
367
|
+
* appended (outer-ish merge so comparison-only buckets still surface).
|
|
368
|
+
*/
|
|
369
|
+
declare function mergeByDimensions(base: Record<string, unknown>[], extra: Record<string, unknown>[], dimensions: string[], valueColumns: string[]): Record<string, unknown>[];
|
|
370
|
+
|
|
371
|
+
declare function compileScopedFilterToSql(filter: FilterCondition, alias: string): {
|
|
372
|
+
sql: string;
|
|
373
|
+
params: unknown[];
|
|
374
|
+
};
|
|
375
|
+
|
|
228
376
|
/**
|
|
229
377
|
* NativeSQLStrategy — Priority 1
|
|
230
378
|
*
|
|
@@ -241,6 +389,16 @@ declare class NativeSQLStrategy implements AnalyticsStrategy {
|
|
|
241
389
|
sql: string;
|
|
242
390
|
params: unknown[];
|
|
243
391
|
}>;
|
|
392
|
+
/**
|
|
393
|
+
* ADR-0021 D-C — inject an object's read scope (tenant + RLS predicate) into
|
|
394
|
+
* the WHERE clause. The scope is a canonical `FilterCondition` (what the
|
|
395
|
+
* RLSCompiler emits); `compileScopedFilterToSql` turns it into alias-qualified,
|
|
396
|
+
* parameterized SQL (fail-closed — it throws rather than drop a predicate).
|
|
397
|
+
* The `?` placeholders are then renumbered into the strategy's `$N` scheme.
|
|
398
|
+
* No-op when the runtime provides no scope hook (the caller is then
|
|
399
|
+
* responsible for isolation — see contract note).
|
|
400
|
+
*/
|
|
401
|
+
private applyReadScope;
|
|
244
402
|
/**
|
|
245
403
|
* Resolve a dimension/measure/filter SQL expression that may reference a
|
|
246
404
|
* related table via dot notation (e.g. `account.industry`).
|
|
@@ -318,4 +476,4 @@ declare class ObjectQLStrategy implements AnalyticsStrategy {
|
|
|
318
476
|
private buildFieldMeta;
|
|
319
477
|
}
|
|
320
478
|
|
|
321
|
-
export { AnalyticsService, type AnalyticsServiceConfig, AnalyticsServicePlugin, type AnalyticsServicePluginOptions, CubeRegistry, NativeSQLStrategy, ObjectQLStrategy };
|
|
479
|
+
export { AnalyticsService, type AnalyticsServiceConfig, AnalyticsServicePlugin, type AnalyticsServicePluginOptions, type CompareTo, type CompiledDataset, CubeRegistry, DatasetExecutor, type DerivedMeasureSpec, NativeSQLStrategy, ObjectQLStrategy, type RelationshipResolver, combineFilters, compileDataset, compileScopedFilterToSql, evaluateDerivedMeasures, mergeByDimensions, shiftRange };
|