@objectstack/service-analytics 9.11.0 → 10.2.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 +60 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +60 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -221,12 +221,31 @@ interface AnalyticsServiceConfig {
|
|
|
221
221
|
* `StrategyContext.coerceTemporalFilterValue` for the full rationale.
|
|
222
222
|
*/
|
|
223
223
|
coerceTemporalFilterValue?: (objectName: string, fieldName: string, value: unknown) => unknown;
|
|
224
|
+
/**
|
|
225
|
+
* ADR-0062 D6 — report whether an object is federated (external datasource).
|
|
226
|
+
* Threaded into the StrategyContext so `NativeSQLStrategy` declines external
|
|
227
|
+
* objects (which it would otherwise query against the wrong physical table),
|
|
228
|
+
* routing them to the driver-correct ObjectQL aggregate path instead. See
|
|
229
|
+
* `StrategyContext.isExternalObject`.
|
|
230
|
+
*/
|
|
231
|
+
isExternalObject?: (objectName: string) => boolean;
|
|
224
232
|
/**
|
|
225
233
|
* ADR-0021 — optional object-graph resolver used when compiling datasets:
|
|
226
234
|
* `(baseObject, relationshipName) => relatedObjectName | undefined`. When
|
|
227
235
|
* provided, `queryDataset` validates that every declared `include` exists.
|
|
228
236
|
*/
|
|
229
237
|
relationshipResolver?: RelationshipResolver;
|
|
238
|
+
/**
|
|
239
|
+
* ADR-0053 currency chain — resolve a measure's SOURCE FIELD currency
|
|
240
|
+
* metadata so a monetary measure that omits an explicit `currency` falls back
|
|
241
|
+
* to the field's declared currency, then the tenant default (`ctx.currency`).
|
|
242
|
+
* Returns the source field's `type` and (fixed-mode) `defaultCurrency`;
|
|
243
|
+
* `undefined` for an unknown field. Non-`currency` fields never get a code.
|
|
244
|
+
*/
|
|
245
|
+
measureCurrency?: (object: string, field: string) => {
|
|
246
|
+
type?: string;
|
|
247
|
+
defaultCurrency?: string;
|
|
248
|
+
} | undefined;
|
|
230
249
|
/** Pre-defined datasets to compile + register at construction (ADR-0021). */
|
|
231
250
|
datasets?: Dataset[];
|
|
232
251
|
/**
|
|
@@ -276,6 +295,7 @@ declare class AnalyticsService implements IAnalyticsService {
|
|
|
276
295
|
private readonly datasetRegistry;
|
|
277
296
|
/** Optional object-graph resolver used when compiling datasets. */
|
|
278
297
|
private readonly relationshipResolver?;
|
|
298
|
+
private readonly measureCurrency?;
|
|
279
299
|
/** Optional dimension display-label resolver (select options / lookup names). */
|
|
280
300
|
private readonly labelResolver?;
|
|
281
301
|
/** ADR-0037 P3: pending-seed row resolver for draft data preview. */
|
package/dist/index.d.ts
CHANGED
|
@@ -221,12 +221,31 @@ interface AnalyticsServiceConfig {
|
|
|
221
221
|
* `StrategyContext.coerceTemporalFilterValue` for the full rationale.
|
|
222
222
|
*/
|
|
223
223
|
coerceTemporalFilterValue?: (objectName: string, fieldName: string, value: unknown) => unknown;
|
|
224
|
+
/**
|
|
225
|
+
* ADR-0062 D6 — report whether an object is federated (external datasource).
|
|
226
|
+
* Threaded into the StrategyContext so `NativeSQLStrategy` declines external
|
|
227
|
+
* objects (which it would otherwise query against the wrong physical table),
|
|
228
|
+
* routing them to the driver-correct ObjectQL aggregate path instead. See
|
|
229
|
+
* `StrategyContext.isExternalObject`.
|
|
230
|
+
*/
|
|
231
|
+
isExternalObject?: (objectName: string) => boolean;
|
|
224
232
|
/**
|
|
225
233
|
* ADR-0021 — optional object-graph resolver used when compiling datasets:
|
|
226
234
|
* `(baseObject, relationshipName) => relatedObjectName | undefined`. When
|
|
227
235
|
* provided, `queryDataset` validates that every declared `include` exists.
|
|
228
236
|
*/
|
|
229
237
|
relationshipResolver?: RelationshipResolver;
|
|
238
|
+
/**
|
|
239
|
+
* ADR-0053 currency chain — resolve a measure's SOURCE FIELD currency
|
|
240
|
+
* metadata so a monetary measure that omits an explicit `currency` falls back
|
|
241
|
+
* to the field's declared currency, then the tenant default (`ctx.currency`).
|
|
242
|
+
* Returns the source field's `type` and (fixed-mode) `defaultCurrency`;
|
|
243
|
+
* `undefined` for an unknown field. Non-`currency` fields never get a code.
|
|
244
|
+
*/
|
|
245
|
+
measureCurrency?: (object: string, field: string) => {
|
|
246
|
+
type?: string;
|
|
247
|
+
defaultCurrency?: string;
|
|
248
|
+
} | undefined;
|
|
230
249
|
/** Pre-defined datasets to compile + register at construction (ADR-0021). */
|
|
231
250
|
datasets?: Dataset[];
|
|
232
251
|
/**
|
|
@@ -276,6 +295,7 @@ declare class AnalyticsService implements IAnalyticsService {
|
|
|
276
295
|
private readonly datasetRegistry;
|
|
277
296
|
/** Optional object-graph resolver used when compiling datasets. */
|
|
278
297
|
private readonly relationshipResolver?;
|
|
298
|
+
private readonly measureCurrency?;
|
|
279
299
|
/** Optional dimension display-label resolver (select options / lookup names). */
|
|
280
300
|
private readonly labelResolver?;
|
|
281
301
|
/** ADR-0037 P3: pending-seed row resolver for draft data preview. */
|
package/dist/index.js
CHANGED
|
@@ -320,6 +320,17 @@ var NativeSQLStrategy = class {
|
|
|
320
320
|
canHandle(query, ctx) {
|
|
321
321
|
if (!query.cube) return false;
|
|
322
322
|
if (query.timeDimensions?.some((td) => !!td.granularity)) return false;
|
|
323
|
+
if (typeof ctx.isExternalObject === "function") {
|
|
324
|
+
const cube = ctx.getCube(query.cube);
|
|
325
|
+
if (cube) {
|
|
326
|
+
if (ctx.isExternalObject(this.extractObjectName(cube))) return false;
|
|
327
|
+
const joinTargets = cube.joins ? Object.values(cube.joins) : [];
|
|
328
|
+
for (const j of joinTargets) {
|
|
329
|
+
const joinedObject = j?.name;
|
|
330
|
+
if (joinedObject && ctx.isExternalObject(joinedObject)) return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
323
334
|
const caps = ctx.queryCapabilities(query.cube);
|
|
324
335
|
return caps.nativeSql && typeof ctx.executeRawSql === "function";
|
|
325
336
|
}
|
|
@@ -1456,6 +1467,7 @@ var AnalyticsService = class {
|
|
|
1456
1467
|
}
|
|
1457
1468
|
this.readScopeProvider = config.getReadScope;
|
|
1458
1469
|
this.relationshipResolver = config.relationshipResolver;
|
|
1470
|
+
this.measureCurrency = config.measureCurrency;
|
|
1459
1471
|
this.labelResolver = config.labelResolver;
|
|
1460
1472
|
this.draftRowsResolver = config.draftRowsResolver;
|
|
1461
1473
|
if (config.datasets) {
|
|
@@ -1476,7 +1488,8 @@ var AnalyticsService = class {
|
|
|
1476
1488
|
// Prefer a compiled dataset's declared relationships (D-C join allowlist);
|
|
1477
1489
|
// fall back to any explicitly-configured provider for legacy cubes.
|
|
1478
1490
|
getAllowedRelationships: (cubeName) => this.datasetRegistry.get(cubeName)?.allowedRelationships ?? config.getAllowedRelationships?.(cubeName),
|
|
1479
|
-
coerceTemporalFilterValue: config.coerceTemporalFilterValue
|
|
1491
|
+
coerceTemporalFilterValue: config.coerceTemporalFilterValue,
|
|
1492
|
+
isExternalObject: config.isExternalObject
|
|
1480
1493
|
};
|
|
1481
1494
|
const builtIn = [
|
|
1482
1495
|
new NativeSQLStrategy(),
|
|
@@ -1633,8 +1646,21 @@ var AnalyticsService = class {
|
|
|
1633
1646
|
}
|
|
1634
1647
|
throw err;
|
|
1635
1648
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1649
|
+
const selectedDims = (selection.dimensions ?? []).map((name) => dataset.dimensions?.find((d) => d.name === name)).filter((d) => !!d);
|
|
1650
|
+
const drillDims = selectedDims.filter((d) => !!d.field && d.type !== "date");
|
|
1651
|
+
if (drillDims.length && result.rows.length) {
|
|
1652
|
+
result.object = dataset.object;
|
|
1653
|
+
result.dimensionFields = Object.fromEntries(
|
|
1654
|
+
drillDims.map((d) => [d.name, d.field])
|
|
1655
|
+
);
|
|
1656
|
+
result.drillRawRows = result.rows.map((row) => {
|
|
1657
|
+
const raw = {};
|
|
1658
|
+
for (const d of drillDims) raw[d.name] = row[d.name];
|
|
1659
|
+
return raw;
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
if (this.labelResolver && selectedDims.length) {
|
|
1663
|
+
const dims = selectedDims.filter((d) => !!d.field).map((d) => ({ name: d.name, field: d.field, type: d.type, dateGranularity: d.dateGranularity }));
|
|
1638
1664
|
if (dims.length) {
|
|
1639
1665
|
try {
|
|
1640
1666
|
await resolveDimensionLabels(dataset.object, dims, result.rows, this.labelResolver);
|
|
@@ -1656,6 +1682,25 @@ var AnalyticsService = class {
|
|
|
1656
1682
|
if (!m) continue;
|
|
1657
1683
|
if (f.label == null && typeof m.label === "string") f.label = m.label;
|
|
1658
1684
|
if (f.format == null && m.format) f.format = m.format;
|
|
1685
|
+
const fc = f;
|
|
1686
|
+
const mc = m;
|
|
1687
|
+
if (fc.currency == null) {
|
|
1688
|
+
const meta = m.field ? this.measureCurrency?.(dataset.object, m.field) : void 0;
|
|
1689
|
+
const monetary = !!mc.currency || meta?.type === "currency";
|
|
1690
|
+
if (monetary) {
|
|
1691
|
+
const resolved = mc.currency ?? meta?.defaultCurrency ?? context?.currency;
|
|
1692
|
+
if (resolved) fc.currency = resolved;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (result.fields?.length && selectedDims.length) {
|
|
1698
|
+
const dimByName = new Map(selectedDims.map((d) => [d.name, d]));
|
|
1699
|
+
const dimByField = new Map(selectedDims.filter((d) => !!d.field).map((d) => [d.field, d]));
|
|
1700
|
+
for (const f of result.fields) {
|
|
1701
|
+
if (f.label != null) continue;
|
|
1702
|
+
const d = dimByName.get(f.name) ?? dimByField.get(f.name);
|
|
1703
|
+
if (d && typeof d.label === "string") f.label = d.label;
|
|
1659
1704
|
}
|
|
1660
1705
|
}
|
|
1661
1706
|
return result;
|
|
@@ -2044,6 +2089,18 @@ var AnalyticsServicePlugin = class {
|
|
|
2044
2089
|
coerceTemporalFilterValue,
|
|
2045
2090
|
relationshipResolver,
|
|
2046
2091
|
labelResolver,
|
|
2092
|
+
// ADR-0053 — source-field currency metadata for the measure currency chain.
|
|
2093
|
+
measureCurrency: (object, field) => {
|
|
2094
|
+
const f = dataEngine()?.getObject?.(object)?.fields?.[field];
|
|
2095
|
+
return f ? { type: f.type, defaultCurrency: f.currencyConfig?.defaultCurrency } : void 0;
|
|
2096
|
+
},
|
|
2097
|
+
// ADR-0062 D6 — a federated object carries an `external` block (ADR-0015).
|
|
2098
|
+
// Reported so NativeSQLStrategy declines it (its hand-compiled FROM would
|
|
2099
|
+
// hit the wrong physical table) and the driver-correct ObjectQL path runs.
|
|
2100
|
+
isExternalObject: (objectName) => {
|
|
2101
|
+
const obj = dataEngine()?.getObject?.(objectName);
|
|
2102
|
+
return !!(obj && obj.external != null);
|
|
2103
|
+
},
|
|
2047
2104
|
draftRowsResolver
|
|
2048
2105
|
};
|
|
2049
2106
|
if (autoBridgedReadScope) {
|