'use strict'; /* *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: Dashboard *| (C) Copyright IBM Corp. 2013, 2020 *| *| US Government Users Restricted Rights - Use, duplication or disclosure *| restricted by GSA ADP Schedule Contract with IBM Corp. *+------------------------------------------------------------------------+ */ // The VisQueryExecution object manages a set of queries that are needed to provide the data for a visualization. define(['jquery', 'underscore', './VisQueryBuilder', '../../../lib/@waca/core-client/js/core-client/ui/core/Class', './CommonQueryBuilder', './legacy/QueryManager', '../../../widgets/livewidget/nls/StringResources', '../../../dataSources/utils/DatasourceUtil', './postprocess/VisQueryPostProcessHelper'], function ($, _, VisQueryBuilder, Class, CommonQueryBuilder, QueryManager, StringResources, DatasourceUtil, PostProcessHelper) { 'use strict'; var VisQueryExecution = null; // class declaration VisQueryExecution = Class.extend({ init: function init(options) { console.debug('visQueryExecution (lw) init'); VisQueryExecution.inherited('init', this, arguments); options = options || {}; this.queryService = options.queryService; this.ownerWidget = options.ownerWidget; this.metadataAPI = options.metadataAPI; this.pageContext = options.pageContext; this.visAPI = options.visAPI; this._commonQueryHelper = options.queryHelper; //Optionally, override this function to perform operations on the data prior to post processing. this._callUnprocessedResultDataHandlers = options.callUnprocessedResultDataHandlers || this._callUnprocessedResultDataHandlers; this._queryManager = new QueryManager({ 'queryService': this.queryService, 'visAPI': this.visAPI, 'dashboardAPI': this.ownerWidget && this.ownerWidget.getDashboardApi && this.ownerWidget.getDashboardApi(), 'postProcessCallback': this.postProcess.bind(this), '_callUnprocessedResultDataHandlers': this._callUnprocessedResultDataHandlers.bind(this) }); if (!this.visQueryBuilder) { var cbGetNLT = this.ownerWidget && this.ownerWidget.getFeature && this.ownerWidget.getFeature.bind(this.ownerWidget, 'vis-chart-insights'); this.visQueryBuilder = new VisQueryBuilder({ 'visAPI': this.visAPI, 'getNLT': cbGetNLT }); } //The _queryChangedCompareString is used to avoid querying in cases where a spec change has no impact on the query definition (or result) this._queryChangedCompareString = null; }, /** * Ensure that the slots are up-to-date and prepare the data (if necessary). * @param localFiltersOnly (optional) if set, only local filters will be passed to the queryDefinition. If not set, both local and cross-widget filters are passed. * @returns a promise. */ execute: function execute(queryContext) { if (queryContext.isInvalidReason) { queryContext.cbQueryFailed({ reason: queryContext.isInvalidReason }); return Promise.reject(); } return this.getQueryOptionsArray(queryContext).then(function (result) { result = result || {}; // Compare with the main data query. // Auxiliary queries (ie. summaries) can be ignored. var aQueryOptionsArray = result.aQueryOptionsArray; if (!queryContext.auxQuery) { this._queryChangedCompareString = this._buildQueryChangedCompareString(aQueryOptionsArray); } return this._doExecute(queryContext, aQueryOptionsArray).then(function (resultData) { if (resultData) { resultData.synchDataFilterEntries = result.synchDataFilterEntries; } return resultData; }); }.bind(this)); }, //This function can be overridden in constructor options to perform tasks on un-postprocessed data. _callUnprocessedResultDataHandlers: function _callUnprocessedResultDataHandlers() {}, postProcess: function postProcess(resultDataEntry) { PostProcessHelper.postProcessMeasuresAsSeries(resultDataEntry, this); PostProcessHelper.postProcessAggregatedSort(resultDataEntry, this); PostProcessHelper.postProcessCustomSort(resultDataEntry, this); PostProcessHelper.postProcessAutobinningAxisLabels(resultDataEntry, this); }, _doExecute: function _doExecute(queryContext, queryOptions) { var _this = this; return this.getQueryManager().whenQueryResultsReady(queryContext.renderContext, queryOptions, queryContext.sender, queryContext.useAPI, queryContext.auxQuery).catch(function (jqXHR) { return Promise.reject(_this._queryFailed(jqXHR)); }); }, getQueryOptionsArray: function getQueryOptionsArray(queryContext) { //lat/long TODO, This is array of query definition //Lat/Long new query Definition Object return this._createQueryDefinition(queryContext).then(function (result) { result = result || {}; var visQueryDefinition = result.visQueryDefinition; var aQueryOptionsArray = []; var oSpecsForQueryDefinition = visQueryDefinition.getQueryDefinitions(); // hand back _.each(oSpecsForQueryDefinition, function (oDef) { var oQueryDefObj = this.getQueryDefinition(); oQueryDefObj.setQueryDataItems(oDef.getDataItems()); oQueryDefObj.setQueryProjections(oDef.getProjections()); //TopBottom queries need to be 'scoped' to items on the current page (tab) oQueryDefObj.setTopBottomQueryPageScope(queryContext.scope); var initialQueryOptions = queryContext.queryOptions ? _.clone(queryContext.queryOptions) : {}; var queryOptions = _.extend(initialQueryOptions, oQueryDefObj.build(initialQueryOptions, this.ownerWidget.visAPI)); // Set filters to query spec var filters = oDef.getFilters(); if (filters && filters.length) { queryOptions.querySpec.filters = filters; } queryOptions.sourceIdOrModule = this.metadataAPI.getModule(); if (queryOptions.querySpec.queryHints) { _.extend(queryOptions.querySpec.queryHints, oDef.getQueryHints()); } else { queryOptions.querySpec.queryHints = oDef.getQueryHints(); } if (queryContext.queryOptions && queryContext.queryOptions.needsUnaggregatedForm) { queryOptions.querySpec.type = 'detail'; } // Pass the functions to handle prompt fault. queryOptions.promptControlFunctions = { 'preparePromptSpec': this.preparePromptSpec.bind(this), 'whenSingleItemQueryReady': this._commonQueryHelper.whenSingleItemQueryReady.bind(this._commonQueryHelper, queryOptions.sourceIdOrModule), 'whenColumnsMinMaxQueryReady': this._commonQueryHelper.whenColumnsMinMaxQueryReady.bind(this._commonQueryHelper, queryOptions.sourceIdOrModule), 'getPromptSpec': this.getPromptSpec.bind(this), 'savePromptSpec': this.savePromptSpec.bind(this), 'updatePromptSpecCache': this.updatePromptSpecCache.bind(this), 'onCancelPromptDialog': this.onCancelPromptDialog.bind(this), 'onUnSupportedPromptType': this.ownerWidget.onUnSupportedPrompt.bind(this, StringResources.get('unSupportedPromptType')) }; queryOptions.layerId = oDef.getLayerId(); if (this.ownerWidget && this.ownerWidget.isPreview) { queryOptions.isPreview = true; } aQueryOptionsArray.push(queryOptions); }, this); return { aQueryOptionsArray: aQueryOptionsArray, synchDataFilterEntries: result.synchDataFilterEntries }; }.bind(this)); }, _buildQueryChangedCompareString: function _buildQueryChangedCompareString(results) { var queryChangedCompareString = ''; _.each(results, function (result) { queryChangedCompareString += JSON.stringify(result.querySpec); }); return queryChangedCompareString; }, queryChanged: function queryChanged(queryContext) { return this.getQueryOptionsArray(queryContext).then(function (result) { var queryUrl = this._buildQueryChangedCompareString(result.aQueryOptionsArray); return { isRenderNeeded: this._queryChangedCompareString !== queryUrl, synchDataFilterEntries: result.synchDataFilterEntries }; }.bind(this)); }, _createQueryDefinition: function _createQueryDefinition() { var _this2 = this; var queryContext = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; queryContext.queryOptions = queryContext.queryOptions || {}; queryContext.queryOptions.addExtraQueryDataItem = CommonQueryBuilder.addExtraQueryDataItem.bind(this, this.ownerWidget.model.getDataItemIdsMap(), queryContext.queryOptions); var visQueryDefinition = this.visQueryBuilder.create(queryContext); queryContext.metadataAPI = this.metadataAPI; var netPageContext = this._getNetPageContext(this.ownerWidget.visAPI.getDefinition().reactToExternalBrushing); var binningInfos = []; var aFilters = CommonQueryBuilder.buildFilterFromPageContext(netPageContext, queryContext, binningInfos); var extraQueryDataItems = queryContext.queryOptions.extraDataItems && queryContext.queryOptions.extraDataItems.length ? queryContext.queryOptions.extraDataItems : null; _.each(visQueryDefinition.getQueryDefinitions(), function (queryDefinition) { _this2._addBinningDataItemSpecIfNeedit(queryDefinition._dataItems, binningInfos); // Add extra query data items required by filter expression, if not already added if (extraQueryDataItems) { queryDefinition._dataItems = _.uniq(queryDefinition._dataItems.concat(extraQueryDataItems), false, function (dataItem) { return dataItem.id; }); } queryDefinition.addFilters(aFilters); }); return Promise.resolve({ visQueryDefinition: visQueryDefinition, synchDataFilterEntries: null }); }, _addBinningDataItemSpecIfNeedit: function _addBinningDataItemSpecIfNeedit(dataItems, binningInfos) { if (binningInfos) { CommonQueryBuilder.addBinningDataItemIfNeedit(dataItems, binningInfos); } }, _isDifferentModule: function _isDifferentModule(queryContext) { queryContext = queryContext || {}; if (!queryContext.sourceIds && !queryContext.assetIds) { return false; } else { return this.ownerWidget.idsNotInModule(queryContext.sourceIds, queryContext.assetIds); } }, _getTableRef: function _getTableRef() { var tableRef = null; var visualization = this.ownerWidget.content.getFeature('Visualization'); var datasource = visualization.getDataSource(); if (datasource) { var slots = visualization.getSlots(); var dataItems = slots.getDataItemList(); tableRef = DatasourceUtil.getTableRef(datasource, dataItems.map(function (dataItem) { return dataItem.getColumnId(); })); } return tableRef; }, _getNetPageContext: function _getNetPageContext(reactToExternalBrushing) { var _this3 = this; var param = { scope: this.ownerWidget.getContainerPageId(), eventGroupId: this.ownerWidget.getEventGroupId(), sourceId: this.metadataAPI.getModule().getSourceId() }; if (reactToExternalBrushing === false) { param.origin = 'filter'; } var netPageContext = this.pageContext && this.pageContext.getNetPageContext(param) || []; // Longer term - this filtering should be removed. The query service will ignore filters that don't apply // (instead of throwing an error) and return messages indicating that certain filters are being ignored. // Shorter term - we may need to compare table references to avoid errors but if there is no table ref, // just let it pass through (e.g. for a global calculation) and let the query service handle it. var tableRef = this._getTableRef(); var isEmptyTableRef = function isEmptyTableRef(aTableRef) { return !aTableRef || _.every(aTableRef, function (ref) { return _.isEmpty(ref); }); }; var haveTableJoin = function haveTableJoin(pageContext) { if (isEmptyTableRef(tableRef)) { return true; } var pageContextTableRef = DatasourceUtil.getPageContextItemTableRef(_this3.metadataAPI.getModule(), pageContext); return isEmptyTableRef(pageContextTableRef) || DatasourceUtil.haveTableJoinsInSameDataSource(_this3.metadataAPI.getModule(), pageContextTableRef, tableRef); }; netPageContext = netPageContext.filter(function (pageContext) { // Don't send anything page context entries that tables are no joined, otherwise get error from such as: // "code": "XQE-MSR-0008", // "details": "XQE-MSR-0008 In module \"newModel\", the following query subjects are not joined: \"Page_1\", \"Page_2\", \"Synch Source 2.xlsx\".\r\n\t // TODO - livewidget_cleanup -- this code used the old slot API -- verifdy if this is this dead code ? return DatasourceUtil.mustVerifyJoinTablesInSameDataSource(_this3.metadataAPI.getModule(), pageContext, _this3.visAPI.getDataSlots()) ? haveTableJoin(pageContext) : true; }); return netPageContext; }, _getNetPageContextFromOtherModules: function _getNetPageContextFromOtherModules() { var _this4 = this; var netPageContext = []; if (this.pageContext) { var currentSourceId = this.metadataAPI.getModule().getSourceId(); var tableRef = this._getTableRef(); var options = { scope: this.ownerWidget.getContainerPageId(), eventGroupId: this.ownerWidget.getEventGroupId(), origin: 'filter' }; var pageContext = this.pageContext.getNetPageContextItems(options); netPageContext = pageContext.filter(function (context) { var otherTableRef = DatasourceUtil.getPageContextItemTableRef(_this4.metadataAPI.getModule(), context.getPageContextSpec()); return context.getSourceId() !== currentSourceId || !DatasourceUtil.haveTableJoinsInSameDataSource(_this4.metadataAPI.getModule(), otherTableRef, tableRef); }); } return netPageContext; }, _queryFailed: function _queryFailed(failure) { var knownFailures = ['unSupportedPromptType', 'cancelPromptDialog', 'promptingIsDisabled']; if (knownFailures.indexOf(failure.message) !== -1) { // this failure will be handled in Explore return failure; } var oDS = this.metadataAPI.getModule(); var msg = 'dwErrorRunningQuery'; var code = void 0; if (failure.reason === 'staleRequest') { msg = 'dwErrorStaleRequest'; } else if (failure.reason === 'geoQueryFail') { msg = 'dwErrorGeoData'; } else if (failure.reason === 'cancelPromptSignon') { msg = 'dwPromptSignonCancelWarning'; } else if (failure.responseJSON && failure.responseJSON.errors) { var oErr = failure.responseJSON.errors[0]; code = oErr && oErr.code; if (code === 'DSS-GEN-0002') { this._hasUnavailableMetadataColumns = true; //M31 FIXME? //this.trigger('hasUnavailableMetadataColumns'); return; } else if (code === 'DSS-GEN-0001') { msg = StringResources.get('errorSourceNotFound', { sourceName: oDS.getSourceName() || oDS.getSourceId() }); } else if (code === 'XQE-PLN-0226') { msg = StringResources.get('errorActionNotSupported', { sourceName: oDS.getSourceName() || oDS.getSourceId() }); } else if (oErr && oErr.message) { msg = oErr.message; } } var oParam = {}; if (oDS) { oParam = { 'datasetName': oDS.getSourceName() || oDS.getSourceId() }; } if (failure.reason !== 'staleRequest') { this.visAPI.setInvalidReason({ msg: msg, code: code }); } return _.extend(new Error(), { msg: msg, param: oParam, hasUnavailableMetadataColumns: this._hasUnavailableMetadataColumns, errorInfo: failure }); }, getQueryService: function getQueryService() { return this.queryService; }, /** * @return the query manager object which owns the query definition and results of * this widget's query. */ getQueryManager: function getQueryManager() { return this._queryManager; }, /** * @returns the query definition object which defines fields for the query. */ getQueryDefinition: function getQueryDefinition() { return this.getQueryManager().getQueryDefinition(); }, /** * @returns the query results object which owns field values from the last executed query */ getQueryResults: function getQueryResults() { return this.getQueryManager().getQueryResults(); }, savePromptSpec: function savePromptSpec(queryOptions) { this.ownerWidget.savePromptSpec(queryOptions); }, getPromptSpec: function getPromptSpec(promptName) { return this.ownerWidget.getPromptSpec(promptName); }, onCancelPromptDialog: function onCancelPromptDialog() { this.ownerWidget.onCancelPromptDialog(); }, updatePromptSpecCache: function updatePromptSpecCache(queryOptions) { var currPromptSpecs = queryOptions && queryOptions.querySpec ? queryOptions.querySpec.parameterValues : null; // When the query is nativeQuery, i.e. internally generated queries, we should not update saved prompts. // If UI knows which prompt is referenced by which data item, nativeQuery can be removed. if (!this.ownerWidget || queryOptions && queryOptions.nativeQuery) { return; } var savedSpecs = this.getPromptSpec(); var newSpecs; if (savedSpecs) { if (currPromptSpecs && currPromptSpecs.length > 0) { var promptNames = _.pluck(currPromptSpecs, 'name'); newSpecs = _.pick(savedSpecs, promptNames); } this.ownerWidget.setPromptSpecs(newSpecs); } }, // this is called by reprompt. getPromptQueries: function getPromptQueries() { var module = this.metadataAPI.getModule(); return { 'whenSingleItemQueryReady': this._commonQueryHelper.whenSingleItemQueryReady.bind(this._commonQueryHelper, module), 'whenColumnsMinMaxQueryReady': this._commonQueryHelper.whenColumnsMinMaxQueryReady.bind(this._commonQueryHelper, module), 'savePromptSpec': this.savePromptSpec.bind(this), 'updatePromptSpecCache': this.updatePromptSpecCache.bind(this) }; }, // TODO This function should be moved to PromptManager once we support OLAP prompts // and get rid of OLAP metadata checking (querySpecInvolvesOlapColumn). preparePromptSpec: function preparePromptSpec(jqXHR) { var errors = jqXHR.responseJSON.errors; var error = errors.length > 0 ? errors[0] : null; if (!error || !error.code) { return; } var params = error.parameters; if (!params || params.length === 0) { return; } var code = error.code; var aParamInfo = []; switch (code) { case 'QF-888': if (this.querySpecInvolvesOlapColumn()) { aParamInfo.push({ involvesOlapColumn: true }); } else { _.each(params, function (param) { param.value.name = param.name; param.value.errorCode = code; // TODO this part should be removed once the prompt fault has the query item ID refering the prompt. param.value.isMultiPrompt = params.length > 1; if (param.value.capabilities.optional) { // Do not prompt for optional prompt and save it with nil value so xqe will ignore this prompt. var optionalPrompt = _.extend(param.value, { values: [{ 'mun': 'nil' }] }); this.ownerWidget.savePromptSpec(optionalPrompt); } else { aParamInfo.push(param.value); } }.bind(this)); } break; default: break; } return aParamInfo; }, querySpecInvolvesOlapColumn: function querySpecInvolvesOlapColumn() { var hasOlapColumn = false; var dataItemAPIs = this.ownerWidget.content.getFeature('Visualization').getSlots().getDataItemList(); if (dataItemAPIs && dataItemAPIs.length > 0) { for (var i = 0; i < dataItemAPIs.length; ++i) { if (this._isOlapColumn(dataItemAPIs[i].getColumnId())) { hasOlapColumn = true; break; } } } return hasOlapColumn; }, _isOlapColumn: function _isOlapColumn(columnId) { var metadataColumn = this.metadataAPI.getMetadataColumn(columnId); return metadataColumn && typeof metadataColumn.isOlapColumn === 'function' ? metadataColumn.isOlapColumn() : false; } }); return VisQueryExecution; }); //# sourceMappingURL=VisQueryExecution.js.map