Sign In

@sisense/sdk-query-client

Package Overview
Dependencies
Maintainers
3
Versions
93
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sisense/sdk-query-client - npm Package Compare versions

Comparing version
2.32.0
to
2.33.0
+1
-1
dist/cjs/dispatcher.d.ts

@@ -8,3 +8,3 @@ /**

*/
export { QueryApiDispatcher } from './query-api-dispatcher/query-api-dispatcher.js';
export { QueryApiDispatcher, shouldUseSearchByDisplayName, } from './query-api-dispatcher/query-api-dispatcher.js';
export type { DisplayNameConfig, GetDataSourceFieldsOptions, } from './query-api-dispatcher/types.js';

@@ -10,4 +10,5 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryApiDispatcher = void 0;
exports.shouldUseSearchByDisplayName = exports.QueryApiDispatcher = void 0;
var query_api_dispatcher_js_1 = require("./query-api-dispatcher/query-api-dispatcher.js");
Object.defineProperty(exports, "QueryApiDispatcher", { enumerable: true, get: function () { return query_api_dispatcher_js_1.QueryApiDispatcher; } });
Object.defineProperty(exports, "shouldUseSearchByDisplayName", { enumerable: true, get: function () { return query_api_dispatcher_js_1.shouldUseSearchByDisplayName; } });

@@ -23,7 +23,13 @@ import { DataSource } from '@sisense/sdk-data';

*
* Calculated dimensions used purely as dimensions (no `filter`) and filters that already carry a
* `datatype` (for example, those created in Fusion) are left untouched. The payload metadata is
* stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and `filter.jaql()`),
* so this is not observable by callers.
* This covers both forms a calculated-dimension filter takes in the payload:
* - a FILTER, whose condition sits at the top level of its own metadata item (`jaql.filter`);
* - a HIGHLIGHT, which `getJaqlQueryPayload` embeds into its dimension as `jaql.in.selected`. Here
* the engine reads the datatype off the DIMENSION element itself, so the dimension element is
* stamped as well as the embedded selection.
*
* Calculated dimensions used purely as dimensions (no `filter`, no highlight) and filters that
* already carry a `datatype` (for example, those created in Fusion) are left untouched. The payload
* metadata is stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and
* `filter.jaql()`), so this is not observable by callers.
*
* @param jaqlPayload - The JAQL payload whose filter metadata is enriched (only `metadata` is read).

@@ -30,0 +36,0 @@ * @param dataSource - The data source the formulas are evaluated against.

@@ -32,7 +32,13 @@ "use strict";

*
* Calculated dimensions used purely as dimensions (no `filter`) and filters that already carry a
* `datatype` (for example, those created in Fusion) are left untouched. The payload metadata is
* stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and `filter.jaql()`),
* so this is not observable by callers.
* This covers both forms a calculated-dimension filter takes in the payload:
* - a FILTER, whose condition sits at the top level of its own metadata item (`jaql.filter`);
* - a HIGHLIGHT, which `getJaqlQueryPayload` embeds into its dimension as `jaql.in.selected`. Here
* the engine reads the datatype off the DIMENSION element itself, so the dimension element is
* stamped as well as the embedded selection.
*
* Calculated dimensions used purely as dimensions (no `filter`, no highlight) and filters that
* already carry a `datatype` (for example, those created in Fusion) are left untouched. The payload
* metadata is stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and
* `filter.jaql()`), so this is not observable by callers.
*
* @param jaqlPayload - The JAQL payload whose filter metadata is enriched (only `metadata` is read).

@@ -46,15 +52,28 @@ * @param dataSource - The data source the formulas are evaluated against.

return __awaiter(this, void 0, void 0, function* () {
const filtersToResolve = jaqlPayload.metadata.filter(isUnresolvedCalculatedDimensionFilter);
yield Promise.all(filtersToResolve.map((filterMetadata) => __awaiter(this, void 0, void 0, function* () {
var _a;
const { formula, context } = filterMetadata.jaql;
// formula/filter presence is guaranteed by isUnresolvedCalculatedDimensionFilter
const parseResponse = yield parseCalculatedDimension(dataSource, formula !== null && formula !== void 0 ? formula : '', context !== null && context !== void 0 ? context : {});
if (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.error) {
throw new translatable_error_js_1.TranslatableError('errors.calculatedDimensionFormulaInvalid', {
message: (_a = parseResponse.message) !== null && _a !== void 0 ? _a : '',
const jaqlsToResolve = jaqlPayload.metadata.flatMap(collectUnresolvedCalculatedDimensionJaqls);
// A highlight produces both an unresolved dimension element and an unresolved embedded selection
// with the same formula/context, so resolve each distinct formula/context once per run and share
// the result across every matching node.
const resolutionByKey = new Map();
const resolveDatatype = (formula, context) => {
const key = JSON.stringify({ formula, context });
let pending = resolutionByKey.get(key);
if (!pending) {
pending = parseCalculatedDimension(dataSource, formula, context).then((parseResponse) => {
var _a;
if (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.error) {
throw new translatable_error_js_1.TranslatableError('errors.calculatedDimensionFormulaInvalid', {
message: (_a = parseResponse.message) !== null && _a !== void 0 ? _a : '',
});
}
return (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.dataType) || DEFAULT_CALCULATED_DIMENSION_DATATYPE;
});
resolutionByKey.set(key, pending);
}
filterMetadata.jaql.datatype =
(parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.dataType) || DEFAULT_CALCULATED_DIMENSION_DATATYPE;
return pending;
};
yield Promise.all(jaqlsToResolve.map((cdJaql) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
// formula presence is guaranteed by needsCalculatedDimensionDatatype
cdJaql.datatype = yield resolveDatatype((_a = cdJaql.formula) !== null && _a !== void 0 ? _a : '', (_b = cdJaql.context) !== null && _b !== void 0 ? _b : {});
})));

@@ -64,15 +83,42 @@ });

/**
* Returns whether a metadata item is a calculated-dimension filter that still needs its result
* data type resolved (a `filter` present, but no top-level `datatype`).
* Collects the calculated-dimension JAQL nodes within a metadata item that still need their result
* data type resolved:
* - the item's own element, when it drives a top-level `filter` OR carries an embedded highlight
* (`jaql.in.selected`) — the engine reads the datatype off this element in both cases;
* - the embedded highlight node itself (`jaql.in.selected.jaql`).
*
* @param metadataItem - The metadata item to test.
* @param metadataItem - The metadata item to inspect.
* @returns The calculated-dimension JAQL nodes (0-2) within the item that still lack a `datatype`.
* @internal
*/
function isUnresolvedCalculatedDimensionFilter(metadataItem) {
var _a;
return (((_a = metadataItem.jaql) === null || _a === void 0 ? void 0 : _a.type) === sdk_data_1.CALCULATED_DIMENSION_JAQL_TYPE &&
function collectUnresolvedCalculatedDimensionJaqls(metadataItem) {
var _a, _b;
const jaql = metadataItem.jaql;
const highlightJaql = (_b = (_a = jaql === null || jaql === void 0 ? void 0 : jaql.in) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b.jaql;
const cdJaqls = [];
// The element drives a filter/highlight when it either owns a top-level filter or carries an
// embedded highlight. A calculated dimension has no `dim`, so it must carry its own `datatype`.
const drivesFilterOrHighlight = Boolean(jaql === null || jaql === void 0 ? void 0 : jaql.filter) || Boolean(highlightJaql === null || highlightJaql === void 0 ? void 0 : highlightJaql.filter);
if (drivesFilterOrHighlight && needsCalculatedDimensionDatatype(jaql)) {
cdJaqls.push(jaql);
}
if (Boolean(highlightJaql === null || highlightJaql === void 0 ? void 0 : highlightJaql.filter) && needsCalculatedDimensionDatatype(highlightJaql)) {
cdJaqls.push(highlightJaql);
}
return cdJaqls;
}
/**
* Returns whether a JAQL node is a calculated dimension that still needs its result data type
* resolved (a calculated dimension with a `formula` but no `datatype`).
*
* @param jaql - The JAQL node to test.
* @returns `true` when the node is a calculated dimension with a `formula` and no `datatype`.
* @internal
*/
function needsCalculatedDimensionDatatype(jaql) {
return ((jaql === null || jaql === void 0 ? void 0 : jaql.type) === sdk_data_1.CALCULATED_DIMENSION_JAQL_TYPE &&
// presence, not truthiness: an empty formula must still reach the parser to surface the
// server's invalid-formula error rather than being silently skipped
metadataItem.jaql.formula !== undefined &&
Boolean(metadataItem.jaql.filter) &&
!metadataItem.jaql.datatype);
jaql.formula !== undefined &&
!jaql.datatype);
}
import { Filter, MetadataItem } from '@sisense/sdk-data';
export declare function applyHighlightFilters(metadataItem: MetadataItem, highlights: Filter[]): MetadataItem;
export declare function matchHighlightsWithAttributes(attributesMetadata: MetadataItem[], highlights: Filter[]): Filter[][];
/**
* Builds the identity key used to match a highlight filter to a query dimension.
*
* Keyed on `dim` for a regular attribute. A calculated dimension has no `dim`, so its `formula` is
* used instead (matching `DimensionalCalculatedAttribute.id`, which returns the formula). Without
* this fallback a calculated-dimension highlight never matches its query dimension and would be
* applied as a standalone slice filter instead of being embedded as a highlight on the dimension.
*
* @param metadataItem - The dimension or filter metadata item to derive an identity from
* @returns The identity key: `dim` (or `formula` for a calculated dimension), suffixed with the
* level and bucket when present.
* @internal
*/
export declare function getMetadataItemId(metadataItem: MetadataItem): string;

@@ -37,5 +37,18 @@ "use strict";

}
/**
* Builds the identity key used to match a highlight filter to a query dimension.
*
* Keyed on `dim` for a regular attribute. A calculated dimension has no `dim`, so its `formula` is
* used instead (matching `DimensionalCalculatedAttribute.id`, which returns the formula). Without
* this fallback a calculated-dimension highlight never matches its query dimension and would be
* applied as a standalone slice filter instead of being embedded as a highlight on the dimension.
*
* @param metadataItem - The dimension or filter metadata item to derive an identity from
* @returns The identity key: `dim` (or `formula` for a calculated dimension), suffixed with the
* level and bucket when present.
* @internal
*/
function getMetadataItemId(metadataItem) {
const { dim, level, dateTimeLevel, bucket } = metadataItem.jaql;
let id = `${dim}`;
const { dim, formula, level, dateTimeLevel, bucket } = metadataItem.jaql;
let id = `${dim !== null && dim !== void 0 ? dim : formula}`;
if (level || dateTimeLevel) {

@@ -42,0 +55,0 @@ id += `_${level || dateTimeLevel}`;

import { DataSource, DataSourceField, DataSourceMetadata, DataSourceSchema } from '@sisense/sdk-data';
import { HttpClient } from '@sisense/sdk-rest-client';
import { CalculatedDimensionParseResponse, CountRowsResponse, JaqlQueryPayload, JaqlResponse, QueryGuid } from '../types.js';
import { GetDataSourceFieldsOptions } from './types.js';
import { DisplayNameConfig, GetDataSourceFieldsOptions } from './types.js';
/**

@@ -16,7 +16,9 @@ * Thin HttpClient-backed datasource/query REST dispatcher.

*
* Endpoint choice depends on system display-name settings (see options.displayNameConfig):
* Path and endpoint selection:
* - When display names are enabled *and* `useNewSearchByDisplayNameApi` is true,
* uses `POST …/fields/searchByDisplayName?isLive=` so search matches display titles
* and the BE routes live vs ElastiCube correctly via `isLive`.
* - Otherwise uses `POST …/fields/search` (viewer-safe field listing used by data browser).
* uses `POST …/fields/searchByDisplayName?isLive=` with the datasource **title**
* (not fullname) so search matches display titles and the BE routes live vs EC via `isLive`.
* - Otherwise uses `POST …/fields/search` with the datasource **fullname** when available
* (from `DataSourceInfo` or the viewer-safe list), encoding path segments while
* preserving `/` — required for EC, EC perspectives, live, and live perspectives.
*

@@ -101,1 +103,12 @@ * @param dataSource - Datasource title or info object

}
/**
* Determines whether to use the display-name field search endpoint.
* Both flags must be true — matching system settings defaults
* (`displayNameConfig.enabled` can be true while `useNewSearchByDisplayNameApi` is still false).
*
* @param displayNameConfig - Optional display-name configuration from system settings.
* @returns `true` when both `enabled` and `useNewSearchByDisplayNameApi` are true,
* selecting the new search-by-display-name API; otherwise `false`.
* @internal
*/
export declare function shouldUseSearchByDisplayName(displayNameConfig?: DisplayNameConfig): boolean;

@@ -13,2 +13,3 @@ "use strict";

exports.QueryApiDispatcher = void 0;
exports.shouldUseSearchByDisplayName = shouldUseSearchByDisplayName;
const sdk_data_1 = require("@sisense/sdk-data");

@@ -30,7 +31,9 @@ const translatable_error_js_1 = require("../translation/translatable-error.js");

*
* Endpoint choice depends on system display-name settings (see options.displayNameConfig):
* Path and endpoint selection:
* - When display names are enabled *and* `useNewSearchByDisplayNameApi` is true,
* uses `POST …/fields/searchByDisplayName?isLive=` so search matches display titles
* and the BE routes live vs ElastiCube correctly via `isLive`.
* - Otherwise uses `POST …/fields/search` (viewer-safe field listing used by data browser).
* uses `POST …/fields/searchByDisplayName?isLive=` with the datasource **title**
* (not fullname) so search matches display titles and the BE routes live vs EC via `isLive`.
* - Otherwise uses `POST …/fields/search` with the datasource **fullname** when available
* (from `DataSourceInfo` or the viewer-safe list), encoding path segments while
* preserving `/` — required for EC, EC perspectives, live, and live perspectives.
*

@@ -42,16 +45,25 @@ * @param dataSource - Datasource title or info object

return __awaiter(this, arguments, void 0, function* (dataSource, options = {}) {
var _a, _b;
var _a, _b, _c;
const { count = 9999, offset = 0, term, displayNameConfig } = options;
const dataSourceName = (0, sdk_data_1.getDataSourceName)(dataSource);
const title = (0, sdk_data_1.getDataSourceName)(dataSource);
const providedFullName = (0, sdk_data_1.getDataSourceFullName)(dataSource);
const useDisplayNameSearch = shouldUseSearchByDisplayName(displayNameConfig);
// Display-name path needs `live`; legacy search needs `fullname`. Skip the list when we already have it.
const needsListLookup = useDisplayNameSearch
? options.live === undefined
: providedFullName === undefined;
const metadata = needsListLookup ? yield this.getDataSourceByTitle(title) : null;
// Fusion: searchByDisplayName → title; fields/search → fullname.
const pathId = useDisplayNameSearch ? title : (_a = providedFullName !== null && providedFullName !== void 0 ? providedFullName : metadata === null || metadata === void 0 ? void 0 : metadata.fullname) !== null && _a !== void 0 ? _a : title;
const encodedPath = (0, sdk_data_1.encodeDataSourcePath)(pathId);
let url;
if (useDisplayNameSearch) {
// `isLive` is required by the BE on this path to pick live vs EC field handlers.
// Prefer an explicit option; otherwise resolve from the viewer-safe list endpoint
// Prefer an explicit option; otherwise list metadata; then caller DataSourceInfo.type
// (do not GET api/datasources/{title} — that requires manage/viewschema and fails for viewers).
const live = (_a = options.live) !== null && _a !== void 0 ? _a : ((_b = (yield this.getDataSourceByTitle(dataSourceName))) === null || _b === void 0 ? void 0 : _b.live) === true;
url = `${API_DATASOURCES_BASE_PATH}/${encodeURIComponent(dataSourceName)}/fields/searchByDisplayName?isLive=${live}`;
const live = (_c = (_b = options.live) !== null && _b !== void 0 ? _b : metadata === null || metadata === void 0 ? void 0 : metadata.live) !== null && _c !== void 0 ? _c : ((0, sdk_data_1.isDataSourceInfo)(dataSource) && dataSource.type === 'live');
url = `${API_DATASOURCES_BASE_PATH}/${encodedPath}/fields/searchByDisplayName?isLive=${live}`;
}
else {
url = `${API_DATASOURCES_BASE_PATH}/${encodeURIComponent(dataSourceName)}/fields/search`;
url = `${API_DATASOURCES_BASE_PATH}/${encodedPath}/fields/search`;
}

@@ -120,5 +132,6 @@ const body = { offset, count };

return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const list = (_a = (yield this.getDataSourceList())) !== null && _a !== void 0 ? _a : [];
return (_b = list.find((ds) => ds.title === title)) !== null && _b !== void 0 ? _b : null;
var _a;
const list = yield this.getDataSourceList();
const items = Array.isArray(list) ? list : [];
return (_a = items.find((ds) => ds.title === title)) !== null && _a !== void 0 ? _a : null;
});

@@ -222,2 +235,5 @@ }

*
* @param displayNameConfig - Optional display-name configuration from system settings.
* @returns `true` when both `enabled` and `useNewSearchByDisplayNameApi` are true,
* selecting the new search-by-display-name API; otherwise `false`.
* @internal

@@ -224,0 +240,0 @@ */

import { Cell, DataCell, Element, QueryResultData } from '@sisense/sdk-data';
import { JaqlResponse } from '../types.js';
export declare const getDataFromQueryResult: (result: JaqlResponse, metadata: Element[]) => QueryResultData;
export declare function prepareResultAsColsAndRows(data: DataCell[][], metadata: Element[]): QueryResultData;
/**
* The subset of an {@link Element} needed to describe a result column.
*
* Query metadata is normally made of full model elements, but columns appended by advanced
* analytics have no element behind them, so only these three fields are ever available.
*/
export type ResultColumnMetadata = Pick<Element, 'name' | 'title' | 'type'>;
export declare const getDataFromQueryResult: (result: JaqlResponse, metadata: readonly ResultColumnMetadata[]) => QueryResultData;
export declare function prepareResultAsColsAndRows(data: DataCell[][], metadata: readonly ResultColumnMetadata[]): QueryResultData;
/**
* Sets the `blur` property for each cell in a 2D array of data cells based on the `selected` property.

@@ -7,0 +14,0 @@ *

@@ -21,2 +21,6 @@ "use strict";

name: d.name,
// `name` carries the element identity (the physical column), so the display label has to
// be surfaced separately for consumers rendering result headers. Model elements always
// resolve `title`, defaulting to `name`.
title: d.title,
type: (0, sdk_data_1.simpleColumnType)(d.type),

@@ -23,0 +27,0 @@ })),

@@ -20,3 +20,3 @@ "use strict";

const query_client_js_1 = require("../query-client.js");
const index_js_1 = require("../query-result/index.js");
const get_data_from_query_result_js_1 = require("../query-result/get-data-from-query-result.js");
const translatable_error_js_1 = require("../translation/translatable-error.js");

@@ -91,7 +91,9 @@ class QueryTaskManager extends task_manager_1.AbstractTaskManager {

const metadata = [...queryDescription.attributes, ...queryDescription.measures];
// extra columns are assumed to have been added by advanced analytics functions
// extra columns are assumed to have been added by advanced analytics functions.
// They have no model element behind them, so the response header doubles as both
// identity and display label.
const extraColumns = (jaqlResponse.headers || [])
.slice(metadata.length)
.map((c) => ({ name: c, type: 'number' }));
return (0, index_js_1.getDataFromQueryResult)(jaqlResponse, [...metadata, ...extraColumns]);
.map((c) => ({ name: c, title: c, type: 'number' }));
return (0, get_data_from_query_result_js_1.getDataFromQueryResult)(jaqlResponse, [...metadata, ...extraColumns]);
});

@@ -98,0 +100,0 @@ }

@@ -8,3 +8,3 @@ /**

*/
export { QueryApiDispatcher } from './query-api-dispatcher/query-api-dispatcher.js';
export { QueryApiDispatcher, shouldUseSearchByDisplayName, } from './query-api-dispatcher/query-api-dispatcher.js';
export type { DisplayNameConfig, GetDataSourceFieldsOptions, } from './query-api-dispatcher/types.js';

@@ -8,2 +8,2 @@ /**

*/
export { QueryApiDispatcher } from './query-api-dispatcher/query-api-dispatcher.js';
export { QueryApiDispatcher, shouldUseSearchByDisplayName, } from './query-api-dispatcher/query-api-dispatcher.js';

@@ -23,7 +23,13 @@ import { DataSource } from '@sisense/sdk-data';

*
* Calculated dimensions used purely as dimensions (no `filter`) and filters that already carry a
* `datatype` (for example, those created in Fusion) are left untouched. The payload metadata is
* stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and `filter.jaql()`),
* so this is not observable by callers.
* This covers both forms a calculated-dimension filter takes in the payload:
* - a FILTER, whose condition sits at the top level of its own metadata item (`jaql.filter`);
* - a HIGHLIGHT, which `getJaqlQueryPayload` embeds into its dimension as `jaql.in.selected`. Here
* the engine reads the datatype off the DIMENSION element itself, so the dimension element is
* stamped as well as the embedded selection.
*
* Calculated dimensions used purely as dimensions (no `filter`, no highlight) and filters that
* already carry a `datatype` (for example, those created in Fusion) are left untouched. The payload
* metadata is stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and
* `filter.jaql()`), so this is not observable by callers.
*
* @param jaqlPayload - The JAQL payload whose filter metadata is enriched (only `metadata` is read).

@@ -30,0 +36,0 @@ * @param dataSource - The data source the formulas are evaluated against.

@@ -10,3 +10,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

};
import { CALCULATED_DIMENSION_JAQL_TYPE } from '@sisense/sdk-data';
import { CALCULATED_DIMENSION_JAQL_TYPE, } from '@sisense/sdk-data';
import { TranslatableError } from '../translation/translatable-error.js';

@@ -30,7 +30,13 @@ /**

*
* Calculated dimensions used purely as dimensions (no `filter`) and filters that already carry a
* `datatype` (for example, those created in Fusion) are left untouched. The payload metadata is
* stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and `filter.jaql()`),
* so this is not observable by callers.
* This covers both forms a calculated-dimension filter takes in the payload:
* - a FILTER, whose condition sits at the top level of its own metadata item (`jaql.filter`);
* - a HIGHLIGHT, which `getJaqlQueryPayload` embeds into its dimension as `jaql.in.selected`. Here
* the engine reads the datatype off the DIMENSION element itself, so the dimension element is
* stamped as well as the embedded selection.
*
* Calculated dimensions used purely as dimensions (no `filter`, no highlight) and filters that
* already carry a `datatype` (for example, those created in Fusion) are left untouched. The payload
* metadata is stamped in place — it is freshly built per query (by `getJaqlQueryPayload` and
* `filter.jaql()`), so this is not observable by callers.
*
* @param jaqlPayload - The JAQL payload whose filter metadata is enriched (only `metadata` is read).

@@ -44,15 +50,28 @@ * @param dataSource - The data source the formulas are evaluated against.

return __awaiter(this, void 0, void 0, function* () {
const filtersToResolve = jaqlPayload.metadata.filter(isUnresolvedCalculatedDimensionFilter);
yield Promise.all(filtersToResolve.map((filterMetadata) => __awaiter(this, void 0, void 0, function* () {
var _a;
const { formula, context } = filterMetadata.jaql;
// formula/filter presence is guaranteed by isUnresolvedCalculatedDimensionFilter
const parseResponse = yield parseCalculatedDimension(dataSource, formula !== null && formula !== void 0 ? formula : '', context !== null && context !== void 0 ? context : {});
if (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.error) {
throw new TranslatableError('errors.calculatedDimensionFormulaInvalid', {
message: (_a = parseResponse.message) !== null && _a !== void 0 ? _a : '',
const jaqlsToResolve = jaqlPayload.metadata.flatMap(collectUnresolvedCalculatedDimensionJaqls);
// A highlight produces both an unresolved dimension element and an unresolved embedded selection
// with the same formula/context, so resolve each distinct formula/context once per run and share
// the result across every matching node.
const resolutionByKey = new Map();
const resolveDatatype = (formula, context) => {
const key = JSON.stringify({ formula, context });
let pending = resolutionByKey.get(key);
if (!pending) {
pending = parseCalculatedDimension(dataSource, formula, context).then((parseResponse) => {
var _a;
if (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.error) {
throw new TranslatableError('errors.calculatedDimensionFormulaInvalid', {
message: (_a = parseResponse.message) !== null && _a !== void 0 ? _a : '',
});
}
return (parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.dataType) || DEFAULT_CALCULATED_DIMENSION_DATATYPE;
});
resolutionByKey.set(key, pending);
}
filterMetadata.jaql.datatype =
(parseResponse === null || parseResponse === void 0 ? void 0 : parseResponse.dataType) || DEFAULT_CALCULATED_DIMENSION_DATATYPE;
return pending;
};
yield Promise.all(jaqlsToResolve.map((cdJaql) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
// formula presence is guaranteed by needsCalculatedDimensionDatatype
cdJaql.datatype = yield resolveDatatype((_a = cdJaql.formula) !== null && _a !== void 0 ? _a : '', (_b = cdJaql.context) !== null && _b !== void 0 ? _b : {});
})));

@@ -62,15 +81,42 @@ });

/**
* Returns whether a metadata item is a calculated-dimension filter that still needs its result
* data type resolved (a `filter` present, but no top-level `datatype`).
* Collects the calculated-dimension JAQL nodes within a metadata item that still need their result
* data type resolved:
* - the item's own element, when it drives a top-level `filter` OR carries an embedded highlight
* (`jaql.in.selected`) — the engine reads the datatype off this element in both cases;
* - the embedded highlight node itself (`jaql.in.selected.jaql`).
*
* @param metadataItem - The metadata item to test.
* @param metadataItem - The metadata item to inspect.
* @returns The calculated-dimension JAQL nodes (0-2) within the item that still lack a `datatype`.
* @internal
*/
function isUnresolvedCalculatedDimensionFilter(metadataItem) {
var _a;
return (((_a = metadataItem.jaql) === null || _a === void 0 ? void 0 : _a.type) === CALCULATED_DIMENSION_JAQL_TYPE &&
function collectUnresolvedCalculatedDimensionJaqls(metadataItem) {
var _a, _b;
const jaql = metadataItem.jaql;
const highlightJaql = (_b = (_a = jaql === null || jaql === void 0 ? void 0 : jaql.in) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b.jaql;
const cdJaqls = [];
// The element drives a filter/highlight when it either owns a top-level filter or carries an
// embedded highlight. A calculated dimension has no `dim`, so it must carry its own `datatype`.
const drivesFilterOrHighlight = Boolean(jaql === null || jaql === void 0 ? void 0 : jaql.filter) || Boolean(highlightJaql === null || highlightJaql === void 0 ? void 0 : highlightJaql.filter);
if (drivesFilterOrHighlight && needsCalculatedDimensionDatatype(jaql)) {
cdJaqls.push(jaql);
}
if (Boolean(highlightJaql === null || highlightJaql === void 0 ? void 0 : highlightJaql.filter) && needsCalculatedDimensionDatatype(highlightJaql)) {
cdJaqls.push(highlightJaql);
}
return cdJaqls;
}
/**
* Returns whether a JAQL node is a calculated dimension that still needs its result data type
* resolved (a calculated dimension with a `formula` but no `datatype`).
*
* @param jaql - The JAQL node to test.
* @returns `true` when the node is a calculated dimension with a `formula` and no `datatype`.
* @internal
*/
function needsCalculatedDimensionDatatype(jaql) {
return ((jaql === null || jaql === void 0 ? void 0 : jaql.type) === CALCULATED_DIMENSION_JAQL_TYPE &&
// presence, not truthiness: an empty formula must still reach the parser to surface the
// server's invalid-formula error rather than being silently skipped
metadataItem.jaql.formula !== undefined &&
Boolean(metadataItem.jaql.filter) &&
!metadataItem.jaql.datatype);
jaql.formula !== undefined &&
!jaql.datatype);
}
import { Filter, MetadataItem } from '@sisense/sdk-data';
export declare function applyHighlightFilters(metadataItem: MetadataItem, highlights: Filter[]): MetadataItem;
export declare function matchHighlightsWithAttributes(attributesMetadata: MetadataItem[], highlights: Filter[]): Filter[][];
/**
* Builds the identity key used to match a highlight filter to a query dimension.
*
* Keyed on `dim` for a regular attribute. A calculated dimension has no `dim`, so its `formula` is
* used instead (matching `DimensionalCalculatedAttribute.id`, which returns the formula). Without
* this fallback a calculated-dimension highlight never matches its query dimension and would be
* applied as a standalone slice filter instead of being embedded as a highlight on the dimension.
*
* @param metadataItem - The dimension or filter metadata item to derive an identity from
* @returns The identity key: `dim` (or `formula` for a calculated dimension), suffixed with the
* level and bucket when present.
* @internal
*/
export declare function getMetadataItemId(metadataItem: MetadataItem): string;

@@ -32,5 +32,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */

}
/**
* Builds the identity key used to match a highlight filter to a query dimension.
*
* Keyed on `dim` for a regular attribute. A calculated dimension has no `dim`, so its `formula` is
* used instead (matching `DimensionalCalculatedAttribute.id`, which returns the formula). Without
* this fallback a calculated-dimension highlight never matches its query dimension and would be
* applied as a standalone slice filter instead of being embedded as a highlight on the dimension.
*
* @param metadataItem - The dimension or filter metadata item to derive an identity from
* @returns The identity key: `dim` (or `formula` for a calculated dimension), suffixed with the
* level and bucket when present.
* @internal
*/
export function getMetadataItemId(metadataItem) {
const { dim, level, dateTimeLevel, bucket } = metadataItem.jaql;
let id = `${dim}`;
const { dim, formula, level, dateTimeLevel, bucket } = metadataItem.jaql;
let id = `${dim !== null && dim !== void 0 ? dim : formula}`;
if (level || dateTimeLevel) {

@@ -37,0 +50,0 @@ id += `_${level || dateTimeLevel}`;

import { DataSource, DataSourceField, DataSourceMetadata, DataSourceSchema } from '@sisense/sdk-data';
import { HttpClient } from '@sisense/sdk-rest-client';
import { CalculatedDimensionParseResponse, CountRowsResponse, JaqlQueryPayload, JaqlResponse, QueryGuid } from '../types.js';
import { GetDataSourceFieldsOptions } from './types.js';
import { DisplayNameConfig, GetDataSourceFieldsOptions } from './types.js';
/**

@@ -16,7 +16,9 @@ * Thin HttpClient-backed datasource/query REST dispatcher.

*
* Endpoint choice depends on system display-name settings (see options.displayNameConfig):
* Path and endpoint selection:
* - When display names are enabled *and* `useNewSearchByDisplayNameApi` is true,
* uses `POST …/fields/searchByDisplayName?isLive=` so search matches display titles
* and the BE routes live vs ElastiCube correctly via `isLive`.
* - Otherwise uses `POST …/fields/search` (viewer-safe field listing used by data browser).
* uses `POST …/fields/searchByDisplayName?isLive=` with the datasource **title**
* (not fullname) so search matches display titles and the BE routes live vs EC via `isLive`.
* - Otherwise uses `POST …/fields/search` with the datasource **fullname** when available
* (from `DataSourceInfo` or the viewer-safe list), encoding path segments while
* preserving `/` — required for EC, EC perspectives, live, and live perspectives.
*

@@ -101,1 +103,12 @@ * @param dataSource - Datasource title or info object

}
/**
* Determines whether to use the display-name field search endpoint.
* Both flags must be true — matching system settings defaults
* (`displayNameConfig.enabled` can be true while `useNewSearchByDisplayNameApi` is still false).
*
* @param displayNameConfig - Optional display-name configuration from system settings.
* @returns `true` when both `enabled` and `useNewSearchByDisplayNameApi` are true,
* selecting the new search-by-display-name API; otherwise `false`.
* @internal
*/
export declare function shouldUseSearchByDisplayName(displayNameConfig?: DisplayNameConfig): boolean;

@@ -10,3 +10,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

};
import { getDataSourceName, } from '@sisense/sdk-data';
import { encodeDataSourcePath, getDataSourceFullName, getDataSourceName, isDataSourceInfo, } from '@sisense/sdk-data';
import { TranslatableError } from '../translation/translatable-error.js';

@@ -27,7 +27,9 @@ const API_DATASOURCES_BASE_PATH = 'api/datasources';

*
* Endpoint choice depends on system display-name settings (see options.displayNameConfig):
* Path and endpoint selection:
* - When display names are enabled *and* `useNewSearchByDisplayNameApi` is true,
* uses `POST …/fields/searchByDisplayName?isLive=` so search matches display titles
* and the BE routes live vs ElastiCube correctly via `isLive`.
* - Otherwise uses `POST …/fields/search` (viewer-safe field listing used by data browser).
* uses `POST …/fields/searchByDisplayName?isLive=` with the datasource **title**
* (not fullname) so search matches display titles and the BE routes live vs EC via `isLive`.
* - Otherwise uses `POST …/fields/search` with the datasource **fullname** when available
* (from `DataSourceInfo` or the viewer-safe list), encoding path segments while
* preserving `/` — required for EC, EC perspectives, live, and live perspectives.
*

@@ -39,16 +41,25 @@ * @param dataSource - Datasource title or info object

return __awaiter(this, arguments, void 0, function* (dataSource, options = {}) {
var _a, _b;
var _a, _b, _c;
const { count = 9999, offset = 0, term, displayNameConfig } = options;
const dataSourceName = getDataSourceName(dataSource);
const title = getDataSourceName(dataSource);
const providedFullName = getDataSourceFullName(dataSource);
const useDisplayNameSearch = shouldUseSearchByDisplayName(displayNameConfig);
// Display-name path needs `live`; legacy search needs `fullname`. Skip the list when we already have it.
const needsListLookup = useDisplayNameSearch
? options.live === undefined
: providedFullName === undefined;
const metadata = needsListLookup ? yield this.getDataSourceByTitle(title) : null;
// Fusion: searchByDisplayName → title; fields/search → fullname.
const pathId = useDisplayNameSearch ? title : (_a = providedFullName !== null && providedFullName !== void 0 ? providedFullName : metadata === null || metadata === void 0 ? void 0 : metadata.fullname) !== null && _a !== void 0 ? _a : title;
const encodedPath = encodeDataSourcePath(pathId);
let url;
if (useDisplayNameSearch) {
// `isLive` is required by the BE on this path to pick live vs EC field handlers.
// Prefer an explicit option; otherwise resolve from the viewer-safe list endpoint
// Prefer an explicit option; otherwise list metadata; then caller DataSourceInfo.type
// (do not GET api/datasources/{title} — that requires manage/viewschema and fails for viewers).
const live = (_a = options.live) !== null && _a !== void 0 ? _a : ((_b = (yield this.getDataSourceByTitle(dataSourceName))) === null || _b === void 0 ? void 0 : _b.live) === true;
url = `${API_DATASOURCES_BASE_PATH}/${encodeURIComponent(dataSourceName)}/fields/searchByDisplayName?isLive=${live}`;
const live = (_c = (_b = options.live) !== null && _b !== void 0 ? _b : metadata === null || metadata === void 0 ? void 0 : metadata.live) !== null && _c !== void 0 ? _c : (isDataSourceInfo(dataSource) && dataSource.type === 'live');
url = `${API_DATASOURCES_BASE_PATH}/${encodedPath}/fields/searchByDisplayName?isLive=${live}`;
}
else {
url = `${API_DATASOURCES_BASE_PATH}/${encodeURIComponent(dataSourceName)}/fields/search`;
url = `${API_DATASOURCES_BASE_PATH}/${encodedPath}/fields/search`;
}

@@ -117,5 +128,6 @@ const body = { offset, count };

return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const list = (_a = (yield this.getDataSourceList())) !== null && _a !== void 0 ? _a : [];
return (_b = list.find((ds) => ds.title === title)) !== null && _b !== void 0 ? _b : null;
var _a;
const list = yield this.getDataSourceList();
const items = Array.isArray(list) ? list : [];
return (_a = items.find((ds) => ds.title === title)) !== null && _a !== void 0 ? _a : null;
});

@@ -218,5 +230,8 @@ }

*
* @param displayNameConfig - Optional display-name configuration from system settings.
* @returns `true` when both `enabled` and `useNewSearchByDisplayNameApi` are true,
* selecting the new search-by-display-name API; otherwise `false`.
* @internal
*/
function shouldUseSearchByDisplayName(displayNameConfig) {
export function shouldUseSearchByDisplayName(displayNameConfig) {
return Boolean((displayNameConfig === null || displayNameConfig === void 0 ? void 0 : displayNameConfig.enabled) && (displayNameConfig === null || displayNameConfig === void 0 ? void 0 : displayNameConfig.useNewSearchByDisplayNameApi));

@@ -223,0 +238,0 @@ }

import { Cell, DataCell, Element, QueryResultData } from '@sisense/sdk-data';
import { JaqlResponse } from '../types.js';
export declare const getDataFromQueryResult: (result: JaqlResponse, metadata: Element[]) => QueryResultData;
export declare function prepareResultAsColsAndRows(data: DataCell[][], metadata: Element[]): QueryResultData;
/**
* The subset of an {@link Element} needed to describe a result column.
*
* Query metadata is normally made of full model elements, but columns appended by advanced
* analytics have no element behind them, so only these three fields are ever available.
*/
export type ResultColumnMetadata = Pick<Element, 'name' | 'title' | 'type'>;
export declare const getDataFromQueryResult: (result: JaqlResponse, metadata: readonly ResultColumnMetadata[]) => QueryResultData;
export declare function prepareResultAsColsAndRows(data: DataCell[][], metadata: readonly ResultColumnMetadata[]): QueryResultData;
/**
* Sets the `blur` property for each cell in a 2D array of data cells based on the `selected` property.

@@ -7,0 +14,0 @@ *

@@ -11,2 +11,6 @@ import { simpleColumnType } from '@sisense/sdk-data';

name: d.name,
// `name` carries the element identity (the physical column), so the display label has to
// be surfaced separately for consumers rendering result headers. Model elements always
// resolve `title`, defaulting to `name`.
title: d.title,
type: simpleColumnType(d.type),

@@ -13,0 +17,0 @@ })),

@@ -15,3 +15,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

import { QUERY_DEFAULT_LIMIT } from '../query-client.js';
import { getDataFromQueryResult } from '../query-result/index.js';
import { getDataFromQueryResult, } from '../query-result/get-data-from-query-result.js';
import { TranslatableError } from '../translation/translatable-error.js';

@@ -86,6 +86,8 @@ export class QueryTaskManager extends AbstractTaskManager {

const metadata = [...queryDescription.attributes, ...queryDescription.measures];
// extra columns are assumed to have been added by advanced analytics functions
// extra columns are assumed to have been added by advanced analytics functions.
// They have no model element behind them, so the response header doubles as both
// identity and display label.
const extraColumns = (jaqlResponse.headers || [])
.slice(metadata.length)
.map((c) => ({ name: c, type: 'number' }));
.map((c) => ({ name: c, title: c, type: 'number' }));
return getDataFromQueryResult(jaqlResponse, [...metadata, ...extraColumns]);

@@ -92,0 +94,0 @@ });

@@ -14,3 +14,3 @@ {

],
"version": "2.32.0",
"version": "2.33.0",
"type": "module",

@@ -42,6 +42,6 @@ "exports": {

"dependencies": {
"@sisense/sdk-common": "2.32.0",
"@sisense/sdk-data": "2.32.0",
"@sisense/sdk-pivot-query-client": "2.32.0",
"@sisense/sdk-rest-client": "2.32.0",
"@sisense/sdk-common": "2.33.0",
"@sisense/sdk-data": "2.33.0",
"@sisense/sdk-pivot-query-client": "2.33.0",
"@sisense/sdk-rest-client": "2.33.0",
"@sisense/task-manager": "^0.1.0",

@@ -48,0 +48,0 @@ "numeral": "^2.0.6",

Sorry, the diff of this file is not supported yet