@hypequery/datasets 0.13.0 → 0.13.2
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/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +22 -7
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +5 -1
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +5 -1
- package/dist/utils/relationship-validation.d.ts +9 -1
- package/dist/utils/relationship-validation.d.ts.map +1 -1
- package/dist/utils/relationship-validation.js +24 -2
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-signature.d.ts","sourceRoot":"","sources":["../../src/cache/query-signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACV,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"query-signature.d.ts","sourceRoot":"","sources":["../../src/cache/query-signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACV,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,CAAC;AAuE3B,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAiBR;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,GAAG,gBAAgB,EACpC,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAkBR"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { getMetricGrain, getMetricRef } from '../utils/metric-handle.js';
|
|
15
15
|
import { getRuntimeTenantPredicate } from '../utils/tenant-runtime.js';
|
|
16
|
+
import { buildRelationshipBuilderContext } from '../utils/relationship-builder-plan.js';
|
|
16
17
|
import { stableStringify } from '../utils/canonical-json.js';
|
|
17
18
|
export { stableStringify };
|
|
18
19
|
const SIGNATURE_VERSION = 1;
|
|
@@ -39,15 +40,29 @@ function orderBySignature(orderBy) {
|
|
|
39
40
|
}));
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
* runtime tenant
|
|
44
|
-
*
|
|
43
|
+
* True when this query joins a tenant-scoped relationship target under an
|
|
44
|
+
* active runtime tenant, so the joined rows are filtered per tenant. Mirrors
|
|
45
|
+
* the executor's join planning (`buildRelationshipBuilderContext`).
|
|
45
46
|
*/
|
|
46
|
-
function
|
|
47
|
+
function activatesTenantScopedJoin(ds, query, context) {
|
|
48
|
+
const joinCtx = buildRelationshipBuilderContext(ds, query, context);
|
|
49
|
+
return !!joinCtx && joinCtx.joins.some((join) => join.tenant);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Tenant portion of the key. A dataset with a `tenantKey` is strictly
|
|
53
|
+
* partitioned per scope. A tenant-less dataset normally shares entries across
|
|
54
|
+
* callers — but if the query joins a tenant-scoped relationship target, those
|
|
55
|
+
* joined rows are still filtered by the runtime tenant, so the key must
|
|
56
|
+
* partition per tenant too or one tenant could be served another's cached rows.
|
|
57
|
+
*/
|
|
58
|
+
function tenantSignature(ds, query, context) {
|
|
59
|
+
const predicate = getRuntimeTenantPredicate(context);
|
|
47
60
|
if (!ds.tenantKey) {
|
|
61
|
+
if (predicate && activatesTenantScopedJoin(ds, query, context)) {
|
|
62
|
+
return { joinScoped: true, operator: predicate.operator, value: predicate.value };
|
|
63
|
+
}
|
|
48
64
|
return null;
|
|
49
65
|
}
|
|
50
|
-
const predicate = getRuntimeTenantPredicate(context);
|
|
51
66
|
if (!predicate) {
|
|
52
67
|
// Trusted cross-tenant scope (`scope: 'all'`) or no runtime tenancy.
|
|
53
68
|
return { key: ds.tenantKey, scope: 'all' };
|
|
@@ -68,7 +83,7 @@ export function buildDatasetQuerySignature(ds, query, context) {
|
|
|
68
83
|
by: query.by ?? null,
|
|
69
84
|
limit: query.limit ?? null,
|
|
70
85
|
offset: query.offset ?? null,
|
|
71
|
-
tenant: tenantSignature(ds, context),
|
|
86
|
+
tenant: tenantSignature(ds, query, context),
|
|
72
87
|
scope: scopeSignature(context),
|
|
73
88
|
});
|
|
74
89
|
}
|
|
@@ -87,7 +102,7 @@ export function buildMetricQuerySignature(metric, query, context) {
|
|
|
87
102
|
by: grain ?? null,
|
|
88
103
|
limit: query.limit ?? null,
|
|
89
104
|
offset: query.offset ?? null,
|
|
90
|
-
tenant: tenantSignature(ref.dataset, context),
|
|
105
|
+
tenant: tenantSignature(ref.dataset, query, context),
|
|
91
106
|
scope: scopeSignature(context),
|
|
92
107
|
});
|
|
93
108
|
}
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAGhB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAEV,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAgB7E,OAAO,EAIL,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAW5B,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
CHANGED
|
@@ -18,7 +18,7 @@ import { applyPagination, overfetchLimit } from './utils/pagination.js';
|
|
|
18
18
|
import { SemanticQueryCache, } from './cache/semantic-query-cache.js';
|
|
19
19
|
import { buildDatasetQuerySignature, buildMetricQuerySignature, } from './cache/query-signature.js';
|
|
20
20
|
import { isQualifiedField, resolveQualifiedField } from './utils/relationship-fields.js';
|
|
21
|
-
import { validateQualifiedFilter } from './utils/relationship-validation.js';
|
|
21
|
+
import { validateQualifiedFilter, validateRelationshipTenantRuntime, } from './utils/relationship-validation.js';
|
|
22
22
|
import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn, } from './utils/relationship-builder-plan.js';
|
|
23
23
|
function validateQuery(metric, query, context) {
|
|
24
24
|
const errors = [];
|
|
@@ -38,6 +38,10 @@ function validateQuery(metric, query, context) {
|
|
|
38
38
|
if (tenantRuntimeError) {
|
|
39
39
|
errors.push(tenantRuntimeError);
|
|
40
40
|
}
|
|
41
|
+
const relationshipTenantError = validateRelationshipTenantRuntime(ds, query, context);
|
|
42
|
+
if (relationshipTenantError) {
|
|
43
|
+
errors.push(relationshipTenantError);
|
|
44
|
+
}
|
|
41
45
|
if (metric.__type === 'grained_metric_ref' && query.by && query.by !== metric.grain) {
|
|
42
46
|
errors.push(`Metric "${ref.name}" is already grained by "${metric.grain}" and cannot be queried with by="${query.by}".`);
|
|
43
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAe9E,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CA8JlB"}
|
|
@@ -2,7 +2,7 @@ import { validateFilterValue } from '../validation.js';
|
|
|
2
2
|
import { SUPPORTED_TIME_GRAINS, isSupportedTimeGrain } from '../constants.js';
|
|
3
3
|
import { getRuntimeTenantPredicate, validateTenantRuntime, } from './tenant-runtime.js';
|
|
4
4
|
import { isQualifiedField, resolveQualifiedField, } from './relationship-fields.js';
|
|
5
|
-
import { validateQualifiedFilter } from './relationship-validation.js';
|
|
5
|
+
import { validateQualifiedFilter, validateRelationshipTenantRuntime, } from './relationship-validation.js';
|
|
6
6
|
export function validateDatasetQueryInput(ds, query, context) {
|
|
7
7
|
const errors = [];
|
|
8
8
|
const dimensionNames = Object.keys(ds.dimensions);
|
|
@@ -19,6 +19,10 @@ export function validateDatasetQueryInput(ds, query, context) {
|
|
|
19
19
|
if (tenantRuntimeError) {
|
|
20
20
|
errors.push(tenantRuntimeError);
|
|
21
21
|
}
|
|
22
|
+
const relationshipTenantError = validateRelationshipTenantRuntime(ds, query, context);
|
|
23
|
+
if (relationshipTenantError) {
|
|
24
|
+
errors.push(relationshipTenantError);
|
|
25
|
+
}
|
|
22
26
|
if (selectedDimensions.length === 0 && selectedMeasures.length === 0) {
|
|
23
27
|
errors.push(`Dataset "${ds.name}" query must select at least one dimension or measure.`);
|
|
24
28
|
}
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
* Shared validation for relationship-qualified filters, used by both the
|
|
3
3
|
* dataset-query and metric-query validators.
|
|
4
4
|
*/
|
|
5
|
-
import type { AnyDatasetInstance, ExecutionContext, MetricFilter } from '../types.js';
|
|
5
|
+
import type { AnyDatasetInstance, DatasetQuery, ExecutionContext, MetricFilter, MetricQuery } from '../types.js';
|
|
6
|
+
type RelationshipQuery = Pick<DatasetQuery & MetricQuery, 'dimensions' | 'filters' | 'orderBy'>;
|
|
7
|
+
/**
|
|
8
|
+
* Tenant-less base datasets may still reach tenant-scoped data through a
|
|
9
|
+
* relationship. Require either a concrete runtime tenant or the trusted
|
|
10
|
+
* cross-tenant scope whenever a query activates such a join.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateRelationshipTenantRuntime(ds: AnyDatasetInstance, query: RelationshipQuery, context?: ExecutionContext): string | undefined;
|
|
6
13
|
/**
|
|
7
14
|
* Validates a relationship-qualified filter (`relationship.field`). Returns an
|
|
8
15
|
* error message string, or null when the filter is valid.
|
|
@@ -12,4 +19,5 @@ import type { AnyDatasetInstance, ExecutionContext, MetricFilter } from '../type
|
|
|
12
19
|
* that column is rejected (the planner injects the tenant predicate instead).
|
|
13
20
|
*/
|
|
14
21
|
export declare function validateQualifiedFilter(ds: AnyDatasetInstance, filter: MetricFilter, context?: ExecutionContext): string | null;
|
|
22
|
+
export {};
|
|
15
23
|
//# sourceMappingURL=relationship-validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-validation.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,
|
|
1
|
+
{"version":3,"file":"relationship-validation.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAWrB,KAAK,iBAAiB,GAAG,IAAI,CAC3B,YAAY,GAAG,WAAW,EAC1B,YAAY,GAAG,SAAS,GAAG,SAAS,CACrC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,iBAAiB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAmBpB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,kBAAkB,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,IAAI,CAgBf"}
|
|
@@ -3,8 +3,30 @@
|
|
|
3
3
|
* dataset-query and metric-query validators.
|
|
4
4
|
*/
|
|
5
5
|
import { validateFilterValue } from '../validation.js';
|
|
6
|
-
import { getRuntimeTenantPredicate } from './tenant-runtime.js';
|
|
7
|
-
import { resolveQualifiedField } from './relationship-fields.js';
|
|
6
|
+
import { getRuntimeTenantPredicate, hasTenantRuntime, } from './tenant-runtime.js';
|
|
7
|
+
import { isQualifiedField, resolveQualifiedField, } from './relationship-fields.js';
|
|
8
|
+
/**
|
|
9
|
+
* Tenant-less base datasets may still reach tenant-scoped data through a
|
|
10
|
+
* relationship. Require either a concrete runtime tenant or the trusted
|
|
11
|
+
* cross-tenant scope whenever a query activates such a join.
|
|
12
|
+
*/
|
|
13
|
+
export function validateRelationshipTenantRuntime(ds, query, context) {
|
|
14
|
+
if (ds.tenantKey || hasTenantRuntime(context)) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const referenced = [
|
|
18
|
+
...(query.dimensions ?? []),
|
|
19
|
+
...(query.filters ?? []).map((filter) => filter.field),
|
|
20
|
+
...(query.orderBy ?? []).map((order) => order.field),
|
|
21
|
+
].filter(isQualifiedField);
|
|
22
|
+
for (const name of referenced) {
|
|
23
|
+
const resolution = resolveQualifiedField(ds, name);
|
|
24
|
+
if (resolution?.resolved?.target.tenantKey) {
|
|
25
|
+
return `Dataset "${ds.name}" requires runtime tenant scoping because relationship "${resolution.resolved.relationshipName}" targets a tenant-scoped dataset.`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
8
30
|
/**
|
|
9
31
|
* Validates a relationship-qualified filter (`relationship.field`). Returns an
|
|
10
32
|
* error message string, or null when the filter is valid.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/datasets",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Semantic layer for defining datasets, metrics, and semantic queries",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@hypequery/protocol": "^0.
|
|
32
|
+
"@hypequery/protocol": "^0.10.0",
|
|
33
33
|
"@noble/hashes": "^1.8.0",
|
|
34
34
|
"zod": "^3.22.4"
|
|
35
35
|
},
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"@types/node": "^18.19.80",
|
|
38
38
|
"typescript": "^5.7.3",
|
|
39
39
|
"@vitest/coverage-v8": "^3.2.6",
|
|
40
|
-
"vitest": "^3.2.6"
|
|
40
|
+
"vitest": "^3.2.6",
|
|
41
|
+
"@hypequery/protocol-conformance": "^0.9.1"
|
|
41
42
|
},
|
|
42
43
|
"repository": {
|
|
43
44
|
"type": "git",
|