'use strict'; /* *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: BI Dashboard *| (C) Copyright IBM Corp. 2017, 2020 *| *| US Government Users Restricted Rights - Use, duplication or disclosure *| restricted by GSA ADP Schedule Contract with IBM Corp. *+------------------------------------------------------------------------+ */ define(['underscore', '../../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../../lib/@waca/upgrades/UpgradeBase', // Ughn - this file should not be in dashboard-core... with dependencies on livewidget... 'dashboard-analytics/dataSources/models/DataSourcesModel', 'dashboard-analytics/dataSources/services/DataSourcesService'], function (_, Class, UpgradeBase, DataSourcesModel, DataSourcesService) { //NOSONAR var UpgradeShaping = Class.extend([UpgradeBase], { init: function init() { this.VERSION = 1005; }, ORIGIN_VISUALIZATION: 'visualization', ORIGIN_FILTER: 'filter', /** * Perform upgrade * * @param {object} spec - spec to perform upgrade on * * @return {Promise} Promise to be resolved when upgrade performed */ up: function up(spec) { this._calcIdMap = {}; this.dataSourcesService = null; this._eventGroupMap = {}; this._assetIdMap = {}; this.spec = spec; this.dataSourcesService = new DataSourcesService({ features: { API: this.data.dashboardApi, Logger: this.data.logger } }); this.spec.dataSources = new DataSourcesModel(this.spec.dataSources); var dataSetShapingsToUpgrade = _.filter(this.spec.datasetShaping, function (datasetShapingInst) { return datasetShapingInst.calculations.length; }); this._buildEventGroupMap(); return this._upgradeShaping(dataSetShapingsToUpgrade).then(this._upgradeFilters.bind(this)).then(this._upgradeShapingFilters.bind(this)).then(function () { this._updateWidgetCalculationReferences(); // remove old shaping delete this.spec.datasetShaping; // For pin upgrade with need the JSON back, not the object if (this.data.pinUpgrade) { if (this.spec.dataSources.toJSON) { this.spec.dataSources = this.spec.dataSources.toJSON(); } // Cleanup any temp modules that were created this.dataSourcesService.destroy(); } Promise.resolve(this.spec); }.bind(this), function (failure) { if (this.data.pinUpgrade) { // Cleanup any temp modules that were created this.dataSourcesService.destroy(); } throw failure; }); }, /** * @returns container Page id containing the layout id * @param widgetId */ getContainerPageId: function getContainerPageId(id) { var topParent = this.findTopLevelParentItem(this.spec.layout, id); if (topParent) { return topParent.id; } return undefined; }, /** * Given any layout item, find its top level parent item (the item in the layout’s root level item list that contains it). * @param layout - this is the layout specification in the board spec * @param id - the id of the model for a widget * @returns the parent object (ie tab) for this model id */ findTopLevelParentItem: function findTopLevelParentItem(layout, id) { var itemFound = null; _.each(layout.items, function (item) { if (item.id === id) { itemFound = layout; } else if (this.findChildItem(item.items, id) !== null) { itemFound = item; } }.bind(this)); return itemFound; }, /** * Recursively search all layout items and their child items lists for an id which matches * the specified id * @param items - the items list whose subtree of items lists are to be searched * @param id - the id to search for. * @returns the item if found or null if this id does not exist in this items list or any child lists. */ findChildItem: function findChildItem(items, id) { if (!items) { return null; } var result = null; for (var i = 0; i < items.length; ++i) { var item = items[i]; if (item.id === id) { // found it! result = item; break; } else if (item.items) { item = this.findChildItem(item.items, id); if (item) { // found it! result = item; break; } // otherwise continue with the remaining siblings } } return result; }, _buildEventGroupMap: function _buildEventGroupMap() { _.each(this.spec.eventGroups, function (eg) { _.each(eg.widgetIds, function (wIds) { this._eventGroupMap[wIds] = eg.id; }.bind(this)); }.bind(this)); }, _upgradeFilters: function _upgradeFilters() { try { var pageContextMap = {}; var promises = []; _.each(this.spec.widgets, function (widget) { var modelRef = widget && widget.data && widget.data.dataViews[0] ? widget.data.dataViews[0].modelRef : null; if (modelRef) { var sourceSpec = _.find(this.spec.dataSources.sources.models, function (source) { return source.id === modelRef; }); promises.push(this._getModule(sourceSpec, false).then(function (module) { if (!this.spec.pageContext) { this.spec.pageContext = []; } if (!_.isEmpty(widget.filters)) { this._buildPageContext(module, pageContextMap, widget, sourceSpec.id); delete widget.filters; } if (!_.isEmpty(widget.localFilters)) { this._updateLocalFilters(module, widget.localFilters); } }.bind(this))); } }.bind(this)); return Promise.all(promises).then(function () { if (!_.isEmpty(pageContextMap)) { this.spec.pageContext = _.values(pageContextMap); } }.bind(this)); } catch (error) { throw error; } }, _upgradeShapingFilters: function _upgradeShapingFilters() { try { var pageContextMap = {}; var promises = []; _.each(this.spec.datasetShaping, function (shaping) { var sourceSpec = _.find(this.spec.dataSources.sources.models, function (source) { return source.assetId === shaping.id; }); if (sourceSpec) { promises.push(this._getModule(sourceSpec, false).then(function (module) { if (!this.spec.pageContext) { this.spec.pageContext = []; } if (!_.isEmpty(shaping.filters)) { this._addShapingFiltersToPageContext(module, pageContextMap, shaping, sourceSpec.id); } delete shaping.filters; }.bind(this))); } }.bind(this)); return Promise.all(promises).then(function () { if (!_.isEmpty(pageContextMap)) { this.spec.pageContext = this.spec.pageContext.concat(_.values(pageContextMap)); } }.bind(this)); } catch (error) { throw error; } }, _updateLocalFilters: function _updateLocalFilters(module, filters) { try { _.each(filters, function (filter) { if (filter.values) { if (filter.values[0] && _.isObject(filter.values[0]) && !filter.values[0].operator) { this._updateFilterUseValues(module, filter, 'object'); } else if (filter.values[0] && typeof filter.values[0] === 'string') { this._updateFilterUseValues(module, filter, 'string'); } else if (!filter.values[0]) { this._updateFilterUseValues(module, filter, 'null'); } else { this._updateLocalFilters(module, filter.values); } } }.bind(this)); } catch (error) { throw error; } }, _addShapingFiltersToPageContext: function _addShapingFiltersToPageContext(module, pageContextMap, shape, sourceId) { _.each(shape.filters, function (filter) { var mappedProps = this._createTuplesAndHierarchies(module, filter, this.ORIGIN_FILTER); var pageContext = { 'origin': this.ORIGIN_FILTER, 'table': '', 'alias': '', 'sourceId': sourceId, 'scope': 'global', 'hierarchyNames': mappedProps.hierarchyNames, 'hierarchyUniqueNames': mappedProps.hierarchies }; if (filter.operator === 'between' || filter.operator === 'notbetween' || filter.operator === 'lt' || filter.operator === 'gt') { pageContext.conditions = this._createCondition(filter); } else { if (filter.operator === 'notin') { pageContext.exclude = true; } pageContext.tupleSet = mappedProps.tupleSet; } var uniqueKey = mappedProps.key + pageContext.scope + pageContext.sourceId; if (_.isEmpty(pageContextMap[uniqueKey])) { pageContextMap[uniqueKey] = pageContext; } }.bind(this)); }, _buildPageContext: function _buildPageContext(module, pageContextMap, widget, sourceId) { _.each(widget.filters, function (filter) { var mappedProps = this._createTuplesAndHierarchies(module, filter, this.ORIGIN_VISUALIZATION); var pageContext = { 'origin': this.ORIGIN_VISUALIZATION, 'table': '', 'alias': '', 'tupleSet': mappedProps.tupleSet, 'sourceId': sourceId, 'hierarchies': mappedProps.hierarchyList, 'hierarchyUniqueNames': mappedProps.hierarchies, 'scope': this.getContainerPageId(widget.id), 'eventSourceId': widget.id, 'eventGroupId': this._eventGroupMap[widget.id] }; var uniqueKey = mappedProps.key + pageContext.eventGroupId + pageContext.scope + pageContext.sourceId; if (_.isEmpty(pageContextMap[uniqueKey])) { pageContextMap[uniqueKey] = pageContext; } }.bind(this)); }, _createCondition: function _createCondition(filter) { var condition = { from: '', to: '', attributeUniqueNames: [filter.columnId] }; if (filter.operator === 'between' || filter.operator === 'notbetween') { condition.from = [filter.values[0].d || filter.values[0].displayValue || filter.values[0]]; condition.to = [filter.values[1].d || filter.values[1].displayValue || filter.values[1]]; } else if (filter.operator === 'lt') { condition.to = [filter.values[0].d || filter.values[0].displayValue || filter.values[0]]; } else { condition.from = [filter.values[0].d || filter.values[0].displayValue || filter.values[0]]; } if (filter.operator === 'notbetween') { condition.invert = true; } return [condition]; }, _createTuplesAndHierarchies: function _createTuplesAndHierarchies(module, filter, origin) { var properties; switch (filter.operator) { case 'in': case 'notin': case 'isnull': case 'between': case 'notbetween': case 'lt': case 'gt': this._updateFilterUseValues(module, filter); properties = this._buildEdgeFilterTupleSet(module, filter, origin); break; case 'or': case 'not': properties = this._buildDataPointFilterTupleSet(module, filter, origin); break; default: properties = { 'tupleSet': '', 'hierachyList': '', 'hierarchies': '' }; break; } return properties; }, _getMetadataColumn: function _getMetadataColumn(module, columnId) { return module ? module.getMetadataColumn(columnId) : null; }, _getMetadataColumnLabel: function _getMetadataColumnLabel(mdColumn) { return mdColumn ? mdColumn.getLabel() : null; }, isFacetEnabled: function isFacetEnabled(mdColumn) { var facetDefinition = mdColumn && mdColumn.getFacetDefinition ? mdColumn.getFacetDefinition() : null; return facetDefinition && facetDefinition.enabled && facetDefinition.enabled.enumValue !== 'false' ? true : false; }, isOlapPackage: function isOlapPackage(mdColumn) { var retval = false; if (mdColumn) { var sourceCategory = mdColumn.getSourceCategory(); retval = sourceCategory && sourceCategory !== 'column' && mdColumn.getObjectType() === 'QueryItem'; } return retval; }, _isDateOrTimeValue: function _isDateOrTimeValue(dateStr) { return isNaN(dateStr) && !isNaN(Date.parse(dateStr)); }, _isDateOrTime: function _isDateOrTime(metadata, value) { return metadata && metadata.isDateOrTimeType() && this._isDateOrTimeValue(value); }, _isRangeOperator: function _isRangeOperator(operator) { return operator && (operator === 'between' || operator === 'notbetween' || operator === 'lt' || operator === 'gt'); }, _isRangeFilter: function _isRangeFilter(metadataColumn, filter, value) { return this._isRangeOperator(filter.operator) && (_.isNumber(value) || this._isDateOrTime(metadataColumn, value)); }, _updateFilterUseValues: function _updateFilterUseValues(module, filter, type) { var metadataColumn = this._getMetadataColumn(module, filter.columnId); var facetEnabled = this.isFacetEnabled(metadataColumn); var isOlap = this.isOlapPackage(metadataColumn); if (filter && filter.values) { var updateValuesList = []; _.each(filter.values, function (value) { var mappedObj = {}; if (type === 'string') { mappedObj.u = value; mappedObj.d = value; } else if (type === 'null') { mappedObj = null; } else { mappedObj.u = !facetEnabled || isOlap || this._isRangeFilter(metadataColumn, filter, value.useValue) || value.useValue === null ? value.useValue : this._mapUseValue(filter.columnId, value.useValue); mappedObj.d = value.displayValue; } updateValuesList.push(mappedObj); }.bind(this)); delete filter.values; filter.values = updateValuesList; } }, _mapUseValue: function _mapUseValue(hun, value) { value = typeof value === 'string' ? value.replace(/]/g, ']]') : value; return hun + '->' + '[' + value + ']'; }, _buildEdgeFilterTupleSet: function _buildEdgeFilterTupleSet(module, filter, origin) { var hunKey = ''; var tupleSet = {}; var hierarchyList = []; var hierarchies = []; var hierarchyNames = []; if (origin === this.ORIGIN_FILTER) { hierarchyNames.push(this._getMetadataColumnLabel(this._getMetadataColumn(module, filter.columnId))); } else { hierarchyList.push({ 'hierarchyUniqueName': filter.columnId }); } _.each(filter.values, function (value) { tupleSet[value.u] = value; hunKey += filter.columnId; hierarchies.push(filter.columnId); }); var filterProps = {}; filterProps.tupleSet = JSON.stringify(tupleSet); filterProps.hierarchies = _.uniq(hierarchies); filterProps.key = hunKey; if (origin === this.ORIGIN_FILTER) { filterProps.hierarchyNames = hierarchyNames; } else if (origin === this.ORIGIN_VISUALIZATION) { filterProps.hierarchyList = hierarchyList; } return filterProps; }, _buildDataPointFilterTupleSet: function _buildDataPointFilterTupleSet(module, filter) { var hunKey = ''; var tupleSet = {}; var hierarchyList = []; var hierarchies = []; if (filter.values) { _.each(filter.values, function (filters) { if (filters.operator === 'and') { this._buildAndFilterTuple(module, tupleSet, hierarchyList, filters); } }.bind(this)); } hierarchyList = _.uniq(hierarchyList); _.each(hierarchyList, function (h) { hierarchies.push({ 'hierarchyUniqueName': h }); hunKey += h; }); return { 'tupleSet': JSON.stringify(tupleSet), 'hierarchyList': _.uniq(hierarchies), 'hierarchies': _.uniq(hierarchyList), 'key': hunKey }; }, _buildAndFilterTuple: function _buildAndFilterTuple(module, tupleSet, hierarchyList, filter) { var tupleKey = ''; var tupleValues = []; _.each(filter.values, function (value) { this._updateFilterUseValues(module, value); _.each(value.values, function (value) { tupleKey += value.u; tupleValues.push(value); }.bind(this)); hierarchyList.push(value.columnId); }.bind(this)); tupleSet[tupleKey] = tupleValues; }, _upgradeShaping: function _upgradeShaping(datasetShapingsToUpgrade) { try { var datasetShapingInst = datasetShapingsToUpgrade.shift(); if (datasetShapingInst) { return this._upgradeShapingInstance(datasetShapingInst).then(function () { this.spec.queriedForUpgrade = true; return this._upgradeShaping(datasetShapingsToUpgrade); }.bind(this)); } else { return Promise.resolve(true); } } catch (error) { throw error; } }, _getSourcesCollection: function _getSourcesCollection(dataSourceModel) { var sourcesCollection = this.dataSourcesService.getSourcesCollection(dataSourceModel); return sourcesCollection.getSources(); }, _getModule: function _getModule(sourceSpec, forceTemp) { if (!sourceSpec) { throw new Error('Could not find source specfication'); } if (forceTemp === true) { sourceSpec.useTempModule = true; } var sources = this._getSourcesCollection(this.spec.dataSources); var existingSource = _.find(sources, function (source) { return source.getAssetId() === sourceSpec.assetId; }); if (!existingSource) { throw new Error('Could not find source module'); } else { var showErrorToast = typeof this.data.showErrorToast === 'undefined' ? true : this.data.showErrorToast; return existingSource.getModule(null, showErrorToast); } }, _upgradeShapingInstance: function _upgradeShapingInstance(datasetShapingInst) { try { var sourceSpec = _.find(this.spec.dataSources.sources.models, function (source) { return source.assetId === datasetShapingInst.id; }); if (!sourceSpec) { return Promise.resolve(true); } return this._getModule(sourceSpec, true).then(function (module) { return this._addCalculation(module, datasetShapingInst.calculations); }.bind(this), function (failure) { if (this.data.pinUpgrade) { // Cleanup any temp modules that were created this.dataSourcesService.destroy(); } throw failure; }.bind(this)); } catch (error) { throw error; } }, _addCalculation: function _addCalculation(module, calculations) { var calculation = calculations.shift(); if (calculation) { // For percent change, the new opereation to use is ¢ var operation = calculation.expr.op === '%d' ? '¢' : calculation.expr.op; var calcProperties = { inputtedName: calculation.label, operation: operation, elementOperands: [], numberOperands: [] }; _.each(calculation.expr.params, function (param) { var metaDataColumn = module.getMetadataColumn(this._calcIdMap[param.col] || param.col); calcProperties.elementOperands.push(metaDataColumn.moserObject); }.bind(this)); // For old % change calculation we need to reverse the array of data items or we won't get the same numbers as in R6. if (operation === '¢') { calcProperties.elementOperands.reverse(); } return module.addCalculation(calcProperties).then(function (newCalculationId) { this._calcIdMap[calculation.id] = newCalculationId; return this._addCalculation(module, calculations); }.bind(this)); } else { return Promise.resolve(true); } }, _updateWidgetCalculationReferences: function _updateWidgetCalculationReferences() { try { _.each(this.spec.widgets, function (widget) { var dataViews = widget.data && widget.data.dataViews ? widget.data.dataViews : []; _.each(dataViews, function (dataView) { _.each(dataView.dataItems, function (dataItem) { dataItem.itemId = this._calcIdMap[dataItem.itemId] || dataItem.itemId; }.bind(this)); }.bind(this)); _.each(widget.localFilters, function (localFilter) { localFilter.columnId = this._calcIdMap[localFilter.columnId] || localFilter.columnId; }.bind(this)); }.bind(this)); } catch (error) { throw error; } }, down: function down(spec) { return Promise.resolve(spec); } }); return new UpgradeShaping(); }); //# sourceMappingURL=waca_shaping.js.map