ShapingModelManager.js 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. 'use strict';
  2. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. /*
  5. *+------------------------------------------------------------------------+
  6. *| Licensed Materials - Property of IBM
  7. *| IBM Cognos Products: Dashboard
  8. *| (C) Copyright IBM Corp. 2017, 2021
  9. *|
  10. *| US Government Users Restricted Rights - Use, duplication or disclosure
  11. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  12. *+------------------------------------------------------------------------+
  13. */
  14. define(['ca-modeller/shaping', 'bi/moser/moser.min', 'underscore', '../../nls/StringResources', '../../metadata/MetadataColumn', '../../utils/DashboardSpecUtil', '../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', '../../../lib/@waca/core-client/js/core-client/ui/dialogs/ConfirmationDialog', '../../../apiHelpers/SlotAPIHelper'], function (Shaping, MoserJS, _, StringResources, MetadataColumn, DashboardSpecUtil, BrowserUtils, ConfirmationDialog, SlotAPIHelper) {
  15. 'use strict';
  16. var MAP_SOURCETYPE_TO_USESPECTYPE = {
  17. uploadedFile: MoserJS.default.UseSpecType.FILE,
  18. 'package': MoserJS.default.UseSpecType.PACKAGE,
  19. dataSet2: MoserJS.default.UseSpecType.DATASET,
  20. module: MoserJS.default.UseSpecType.MODULE,
  21. data_asset: MoserJS.default.UseSpecType.WA_DATA_ASSET,
  22. url: MoserJS.default.UseSpecType.URL,
  23. report: MoserJS.default.UseSpecType.REPORT,
  24. socialMediaProject: MoserJS.default.UseSpecType.SOCIAL_MEDIA
  25. };
  26. var SOURCE_TYPES_TO_UPDATE = ['dataset2', 'uploadedFile', 'module'];
  27. var UNWANTED_MOSER_OBJECT_TYPES_FOR_METADATACOLUMNS = [MoserJS.default.MoserObjectTypes.ITEM_TYPE, MoserJS.default.MoserObjectTypes.QUERY_SUBJECT, MoserJS.default.MoserObjectTypes.FOLDER, MoserJS.default.MoserObjectTypes.DEF_LINK_TYPE];
  28. var ShapingModelManager = function () {
  29. function ShapingModelManager(options) {
  30. _classCallCheck(this, ShapingModelManager);
  31. this.dashboardApi = options.dashboardApi;
  32. this.sourceModel = options.sourceModel;
  33. this.logger = this.dashboardApi.getGlassCoreSvc('.Logger');
  34. this.queryService = this.dashboardApi.getFeature('QueryService');
  35. this.dashboardApi.on('mode:change', this.onModeChange, this);
  36. if (options.sourceModel) {
  37. this.shapingModel = options.sourceModel.get('shaping');
  38. this.shapingModel.setShapingModelManager(this);
  39. }
  40. this.sourceModelManager = options.sourceModelManager;
  41. this.metadataColumnCache = {};
  42. // Used to track what kind of module we're dealing with
  43. this._embeddedModule = true;
  44. this._queryTypeParam = 'module';
  45. this._loadedMetadataColumns = {};
  46. }
  47. ShapingModelManager.prototype.destroy = function destroy() {
  48. this._shapingHelper.destroy();
  49. this._shapingHelper = null;
  50. this.module = null;
  51. this.tempModuleLoaded = null;
  52. };
  53. ShapingModelManager.prototype.isTemporaryModule = function isTemporaryModule() {
  54. return this._embeddedModule;
  55. };
  56. /**
  57. * Changing dashboard modes authoring to consume or consume to authoring
  58. */
  59. ShapingModelManager.prototype.onModeChange = function onModeChange(payload) {
  60. if (payload && payload.authoring) {
  61. return this.ensureTemporaryModule();
  62. }
  63. return Promise.resolve(this.module);
  64. };
  65. /**
  66. * Ensures we are working against a temporary module
  67. */
  68. ShapingModelManager.prototype.ensureTemporaryModule = function ensureTemporaryModule() {
  69. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  70. if (this.isTemporaryModule()) {
  71. return Promise.resolve(this.module);
  72. } else {
  73. return this._loadModule(undefined, options).then(this._setModule.bind(this)).then(this.whenTemporaryModuleReady.bind(this)).catch(this._handleLoadError.bind(this, false));
  74. }
  75. };
  76. ShapingModelManager.prototype.addWADerivedAttribute = function addWADerivedAttribute(derivedAttribute) {
  77. this._initializeFormulaeConverter();
  78. if (this.formulaeConverter && this.attributeProcessor) {
  79. var qs = this.module.getQuerySubject()[0];
  80. var v5Exp = this.formulaeConverter.formulaeToV5Expression(derivedAttribute.formulae, MoserJS.default.ExpressionType.CALCULATION);
  81. var factory = MoserJS.default.createObjectFactory();
  82. var qi = factory.createQueryItem();
  83. qi.setFacetDefinition(factory.createFacetType());
  84. MoserJS.default.ModuleUtils.generateObjectId(qs, qi);
  85. var prop = factory.createPropertyType();
  86. prop.setName('wa_attributeUniqueName');
  87. prop.setValue(derivedAttribute.uniqueName);
  88. qi.addProperty(prop);
  89. // Until MUI calls validate when adding a query item
  90. //qi.setDatatype('NVARCHAR(100)');
  91. qi.setLabel(derivedAttribute.caption);
  92. qi.setExpression(v5Exp);
  93. this.attributeProcessor.execute(qi, derivedAttribute.formulae[0]);
  94. // Until we have the latest MUI
  95. if (this._shapingHelper.expressionAddToModuleWithValidation) {
  96. this._shapingHelper.expressionAddToModuleWithValidation(qi, qs);
  97. }
  98. // big hack - until MUI marks the module as dirty after adding a query item
  99. //this._shapingHelper.context.store.sessionSave.dirtyTracker._dirtyPointer++;
  100. }
  101. return Promise.resolve();
  102. };
  103. ShapingModelManager.prototype._initializeFormulaeConverter = function _initializeFormulaeConverter() {
  104. if (!this.formulaeConverter && this.module) {
  105. if (MoserJS.default.FormulaeV5Converter) {
  106. this.formulaeConverter = new MoserJS.default.FormulaeV5Converter();
  107. this.formulaeConverter.initWithModule(this.module);
  108. }
  109. if (MoserJS.default.AttributeProcessor) {
  110. this.attributeProcessor = new MoserJS.default.AttributeProcessor();
  111. this.attributeProcessor.initWithModule(this.module);
  112. }
  113. }
  114. };
  115. ShapingModelManager.prototype._setModule = function _setModule(module) {
  116. if (this.module) {
  117. this.clearCache();
  118. this.module.removeListener(this.moduleEventListener.bind(this));
  119. }
  120. this.module = module;
  121. module.addListener(this.moduleEventListener.bind(this));
  122. return module;
  123. };
  124. /**
  125. * Creates a shapingHelper and moserJS object. Also makes sure the metadata is loaded
  126. * @param option module id that can be used to load the module if it already exist in the temporary session
  127. * @return {Promise} Promise that will be resolve when the module is finished loading
  128. */
  129. ShapingModelManager.prototype.load = function load(moduleId, showErrorToast) {
  130. if (this._shapingHelper) {
  131. return Promise.resolve();
  132. }
  133. return this._createShapingHelper().then(this._loadModule.bind(this, moduleId)).then(this._setModule.bind(this)).catch(this._handleLoadError.bind(this, showErrorToast));
  134. };
  135. ShapingModelManager.prototype._createShapingHelper = function _createShapingHelper() {
  136. var _this = this;
  137. return this.dashboardApi.getGlassSvc('.DataConnectionServiceFactory').then(function (dataConnectionServiceFactory) {
  138. return dataConnectionServiceFactory.getDataConnectionResolver({
  139. onAmbiguousConnectionResolved: function onAmbiguousConnectionResolved() {
  140. _this.dashboardApi.triggerDashboardEvent('ambiguousConnection:resolved', {
  141. sourceId: _this.sourceModel.id
  142. });
  143. }
  144. });
  145. }).then(function (dataConnectionService) {
  146. var DndManager = _this.dashboardApi.getFeature('DashboardDnd.internal');
  147. var enableNavigationGroupAction = !(_this.sourceModel.getIsOlapPackage() || _this.sourceModelManager.getType() === 'data_asset');
  148. var enableCustomGroups = !_this.dashboardApi.getGlassCoreSvc('.FeatureChecker').checkValue('dashboard', 'customGroups', 'disabled');
  149. var enableOlapMember = _this.dashboardApi.getGlassCoreSvc('.FeatureChecker').checkValue('dashboard', 'showMembers', 'enabled');
  150. var options = {};
  151. _this.dashboardApi.prepareGlassOptions(options);
  152. _this._shapingHelper = new Shaping.Shaping(options.glassContext, {
  153. glassDnD: DndManager,
  154. undoCallback: _this.addToUndoStack.bind(_this),
  155. confirmations: {
  156. removeFromModule: _this._removeItemsFromModule.bind(_this)
  157. },
  158. featureConfig: {
  159. filterExpressionCreateAction: true,
  160. filterExpressionEditAction: true,
  161. filterExpressionRemoveAction: true,
  162. calculationAction: true,
  163. includeImportedObjects: false,
  164. removeAction: true,
  165. expressionEditorEditAction: true,
  166. expressionEditorAction: true,
  167. filterAction: true,
  168. navigationGroupAction: enableNavigationGroupAction,
  169. sortAction: true,
  170. filterIndividualValues: false,
  171. showFullPackage: true,
  172. showMembers: enableOlapMember || !!_this.dashboardApi.getAppConfig('showMembers'),
  173. formatAction: true,
  174. useValidationDialog: true,
  175. scrollableTrees: BrowserUtils.isIPad(),
  176. showModuleTreeNode: false,
  177. customGroupCreateAction: enableCustomGroups,
  178. customGroupReplaceAction: enableCustomGroups
  179. },
  180. connectionResolver: dataConnectionService
  181. });
  182. _this._shapingHelper.context.shapingHelper = _this._shapingHelper; //Add the shapingHelper to the context so that it can be accessed by context menu operations.
  183. _this.shapingModel.setShapingHelper(_this._shapingHelper);
  184. }).catch(function (error) {
  185. if (_this.logger) {
  186. _this.logger.error(error, _this);
  187. }
  188. });
  189. };
  190. // Callback function to resolve either true or false so MUI will contiue or cancel the remove action.
  191. ShapingModelManager.prototype._removeItemsFromModule = function _removeItemsFromModule() {
  192. return new Promise(function (resolve) {
  193. var oConfirmationDialog = new ConfirmationDialog('warning', StringResources.get('delete_confirm'), StringResources.get('calculation_navigation_in_use'));
  194. oConfirmationDialog.confirm(function () {
  195. resolve(true);
  196. }, function () {
  197. resolve(false);
  198. });
  199. });
  200. };
  201. /**
  202. * Ensure the temporary session module is up-to-date for external components to consume
  203. * @returns {Promise<String>} Promises a module id which can be either:
  204. * - Temporary session module id (authoring)
  205. * - Embedded module id (consumption on shaped data source)
  206. * - Original assert id (consumption on non-shaped data source)
  207. */
  208. ShapingModelManager.prototype.saveTemporaryModule = function saveTemporaryModule() {
  209. var _this2 = this;
  210. if (this.tempModuleLoaded) {
  211. return this.tempModuleLoaded.then(function () {
  212. return _this2._shapingHelper.saveSessionModule();
  213. });
  214. }
  215. return Promise.resolve(this._shapingHelper && this._shapingHelper.getModuleId());
  216. };
  217. /**
  218. * Clears any previously loaded temporary module
  219. */
  220. ShapingModelManager.prototype.clearTemporaryModule = function clearTemporaryModule() {
  221. this.tempModuleLoaded = null;
  222. };
  223. /**
  224. * When a temporary module is loaded and completed
  225. * @return {Promise} resolved when temporary module is loaded or temporary module is not used
  226. */
  227. ShapingModelManager.prototype.whenTemporaryModuleReady = function whenTemporaryModuleReady() {
  228. return this.tempModuleLoaded || Promise.resolve();
  229. };
  230. /**
  231. * Creates a moserJS module object using the shapingHelper
  232. * @param {Object} moduleId option id to be used to load the module from - used if we already have the temporary module
  233. * @param {Object} options.forceTemporaryModule --> Force temp module; even in consume
  234. * @return {Promise} A promise which will get resolved with the module object
  235. */
  236. ShapingModelManager.prototype._loadModule = function _loadModule(moduleId) {
  237. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { forceTemporaryModule: false };
  238. // Most paths leads us to a temp module, default it to true
  239. this._embeddedModule = true;
  240. this._queryTypeParam = 'module';
  241. var isAuthoringMode = this.dashboardApi.getMode() === 'authoring';
  242. var optimizedMetadataIdList = this.getMetadataSubsetIds();
  243. var forceTemporaryModule = options.forceTemporaryModule;
  244. if (this.sourceModel.hasInlineModule()) {
  245. //////////////////////////
  246. // # CASE 5: Inline module spec for CDE
  247. return this._shapingHelper.createInlineModule(this.sourceModel.module, this.shapingModel.moserJSON);
  248. } else {
  249. //////////////////////////
  250. // # CASE 4: Data source IS shaped, dashboard is loaded in authoring mode.
  251. // Temporary session module is created based off the embedded module
  252. if (this.shapingModel.moserJSON && (!isAuthoringMode && forceTemporaryModule || !this.shapingModel.embeddedModuleUpToDate || isAuthoringMode)) {
  253. this.tempModuleLoaded = this._shapingHelper.loadFromJSON(this.shapingModel.moserJSON);
  254. return this.tempModuleLoaded;
  255. }
  256. if (moduleId) {
  257. this.shapingModel.set({
  258. shapingId: '__initialShapingID__'
  259. }, {
  260. silent: true
  261. });
  262. //TODO Verify the use case and validate the inputs
  263. return this._shapingHelper.loadFromModuleId(moduleId, this.sourceModel.type, optimizedMetadataIdList);
  264. }
  265. //////////////////////////
  266. // # CASE 3: Data source is NOT shaped, dashboard is loaded in authoring mode.
  267. // Temporary session module is created based off the original asset
  268. // Create a temp module if we're in authoring and useTempModule wasn't set to false, or if useTempModule was set to true
  269. if (!isAuthoringMode && forceTemporaryModule || isAuthoringMode && this.sourceModel.useTempModule !== false || this.sourceModel.useTempModule === true) {
  270. this.tempModuleLoaded = this._shapingHelper.addSource({
  271. id: this.sourceModel.get('assetId'),
  272. href: this.sourceModel.get('searchPath'),
  273. type: MAP_SOURCETYPE_TO_USESPECTYPE[this.sourceModel.get('type')],
  274. name: this.sourceModel.get('name')
  275. });
  276. return this.tempModuleLoaded;
  277. }
  278. //////////////////////////
  279. // # CASE 2: Data source IS shaped, dashboard is loaded in consumption mode.
  280. // Embedded module is loaded. Embedded module is stored as an attachment of the dashboard in CM.
  281. this._embeddedModule = false;
  282. if (this.shapingModel.embeddedModuleId) {
  283. return this._shapingHelper.loadFromModuleId(this.shapingModel.embeddedModuleId, 'module', optimizedMetadataIdList);
  284. }
  285. //////////////////////////
  286. // # CASE 1: Data source is NOT shaped, dashboard is loaded in consumption mode.
  287. // We need to pass to MUI the right datasource type that is used inside Moser 'useSpec', instead of the CM datasource type
  288. var useSpecSourceType = MAP_SOURCETYPE_TO_USESPECTYPE[this.sourceModel.get('type')].value();
  289. this._queryTypeParam = useSpecSourceType;
  290. return this._shapingHelper.loadFromModuleId(this.sourceModel.assetId, useSpecSourceType, optimizedMetadataIdList);
  291. }
  292. };
  293. /**
  294. * Called by the tree/grid when a shaping change happens so we can add to our undo/redo stack
  295. * @param {Object} undoRedoObj Object that has an undo and redo method that we can call
  296. * @param {Object} payloadData Optional payload data
  297. */
  298. ShapingModelManager.prototype.addToUndoStack = function addToUndoStack(undoRedoObj, payloadData) {
  299. var newShapingId = this.shapingModel._generateUniqueId('shaping');
  300. // Will be passed back in the onUndoRedoShapingChange function call
  301. var callbackPayload = {
  302. undoRedoObj: undoRedoObj,
  303. prevValue: this.shapingModel.get('shapingId'),
  304. value: newShapingId
  305. };
  306. var payload = {
  307. sender: 'ShapingModelManager',
  308. payloadData: payloadData ? payloadData : null,
  309. senderContext: {
  310. applyFn: this._onUndoRedoShapingChange.bind(this, callbackPayload)
  311. }
  312. };
  313. this.shapingModel.set({
  314. shapingId: newShapingId
  315. }, payload);
  316. };
  317. /**
  318. * Called by our undoRedoController when the user does an undo/redo of a shaping change
  319. * @param {Object} callbackPayload Object that was build when adding the information to the undo stack
  320. * @param {String} value the new value for the shapingId
  321. */
  322. ShapingModelManager.prototype._onUndoRedoShapingChange = function _onUndoRedoShapingChange(callbackPayload, value) {
  323. if (callbackPayload.prevValue === value) {
  324. callbackPayload.undoRedoObj.undo();
  325. } else {
  326. callbackPayload.undoRedoObj.redo();
  327. }
  328. // Since we're overriding the default handling of undo/redo make sure we update the model with the value
  329. this.shapingModel.set({
  330. shapingId: value
  331. }, {
  332. payloadData: {
  333. skipUndoRedo: true
  334. }
  335. });
  336. };
  337. ShapingModelManager.prototype.getShapingHelper = function getShapingHelper() {
  338. return this._shapingHelper;
  339. };
  340. ShapingModelManager.prototype.getLocalizedName = function getLocalizedName() {
  341. return this.sourceModelManager.getLocalizedName();
  342. };
  343. /**
  344. * Handles showing error messages when the module fails to load
  345. * @return {Promise} Promise that will resolve with an augmented error object once the message is shown
  346. */
  347. ShapingModelManager.prototype._handleLoadError = function _handleLoadError(showErrorToast, err) {
  348. var _this3 = this;
  349. // If the temp module failed to load make sure we don't think we have one or we'll end up deleting the source module.
  350. this._embeddedModule = false;
  351. this.sourceModel.set({
  352. state: 'error',
  353. errorCode: err.code
  354. });
  355. return this.getLocalizedName().then(function (name) {
  356. var exists = _this3.sourceModelManager.cmObjectExists;
  357. err.sourceInfo = {
  358. exists: exists,
  359. name: name
  360. };
  361. if (showErrorToast !== false) {
  362. // The missingDataSetData message should be displayed for error code MSR-GEN-0091 (data for the file does not exist)
  363. if (_this3._isMissingDatasetData()) {
  364. _this3._showToast(StringResources.get('missingDataSetData'), {
  365. type: 'warning',
  366. preventDuplicates: true
  367. });
  368. } else {
  369. _this3._showToast(StringResources.get('errorLoadingSource', {
  370. sourceName: name
  371. }));
  372. }
  373. }
  374. return err;
  375. }).catch(function () {
  376. err.sourceInfo = {
  377. exists: false,
  378. name: _this3.sourceModel.name
  379. };
  380. if (showErrorToast !== false) {
  381. if (_this3.sourceModel.name) {
  382. _this3._showToast(StringResources.get('errorLoadingSource', {
  383. sourceName: _this3.sourceModel.name
  384. }));
  385. } else {
  386. _this3._showToast(StringResources.get('errorLoadingSourceMetaData'));
  387. }
  388. }
  389. return err;
  390. }).finally(function () {
  391. throw err;
  392. });
  393. };
  394. ShapingModelManager.prototype._isMissingDatasetData = function _isMissingDatasetData() {
  395. return this.sourceModel.get('errorCode') === 'MSR-GEN-0091' || this.sourceModelManager.getState() === 'error' && this.sourceModelManager.getType() === 'dataSet2' && this.sourceModelManager.cmObjectExists;
  396. };
  397. ShapingModelManager.prototype._showToast = function _showToast(msg, options) {
  398. var toastOptions = options || {
  399. type: 'error',
  400. preventDuplicates: true
  401. };
  402. this.dashboardApi.showToast(msg, toastOptions);
  403. };
  404. /**
  405. * @param {Object} dashboardInfo object containing optional information used to create headers in dss requestedVersion
  406. * @param isDirty - if the dashboard is currently in an unsaved state
  407. * @param dashboardId - dashboard store id
  408. * @param searchPath - dashboard search path.
  409. * @param {String} dataSearchPath - model search path
  410. * @return {Object} object which contains ajax options, such as headers
  411. */
  412. ShapingModelManager.prototype._getDashboardOptions = function _getDashboardOptions(dashboardInfo, dataSearchPath, options) {
  413. var dbOptions = {};
  414. dbOptions.ajaxOptions = {
  415. headers: {
  416. 'x-audit-type': dashboardInfo.type,
  417. // Only add id and search path if the dashboard is not in the dirty state
  418. 'x-audit-dashboardId': dashboardInfo.isDirty ? null : dashboardInfo.boardId,
  419. 'x-audit-searchPath': dashboardInfo.isDirty ? null : encodeURI(dashboardInfo.searchPath),
  420. 'x-audit-dataSearchPath': encodeURI(dataSearchPath),
  421. 'x-senderId': options ? options.senderId : null
  422. }
  423. };
  424. if (options && options.lastModified) {
  425. dbOptions.ajaxOptions.headers['x-model-last-modified'] = options.lastModified;
  426. }
  427. if (options && options.queryUserId) {
  428. dbOptions.ajaxOptions.headers['x-queryuserid'] = options.queryUserId;
  429. }
  430. return dbOptions;
  431. };
  432. /**
  433. * Queries DSS for data
  434. * @param {String} querySpec the query specification sent to DSS
  435. * @param {Object} options
  436. *
  437. * @return {Promise}
  438. */
  439. ShapingModelManager.prototype.queryData = function queryData(querySpec, options) {
  440. return this._queryData(querySpec, this._shapingHelper.getData.bind(this._shapingHelper), options);
  441. };
  442. /**
  443. * Query DSS for predict data
  444. * @param {String} querySpec the predict query specification sent to DSS
  445. * @param {Object} options
  446. *
  447. * @return {Promise}
  448. */
  449. ShapingModelManager.prototype.queryPredictData = function queryPredictData(querySpec, options) {
  450. return this._queryData(querySpec, this._shapingHelper.getPredictData.bind(this._shapingHelper), options);
  451. };
  452. /**
  453. * Query relationship (Smarts Explore)
  454. * @param {JSONObject} querySpec
  455. * @param {Object} options
  456. *
  457. * @return {Promise}
  458. */
  459. ShapingModelManager.prototype.querySmartsExploreRelationshipData = function querySmartsExploreRelationshipData(querySpec, options) {
  460. //TODO: Currently, this is called by Explorer to query smarts/relatonship endpoint.
  461. //Before a common 'prompt error response handler component' is implmented. Explorer
  462. //forwards queryspec to MUI and delegates MUI to send the request so that it can reuse the
  463. //DB signon and ambiguous connections signon handlers.
  464. return this._queryData(querySpec, this._shapingHelper.getExploreRelationshipData.bind(this._shapingHelper), options);
  465. };
  466. ShapingModelManager.prototype._queryData = function _queryData(querySpec, getData) {
  467. var _this4 = this;
  468. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  469. return this.load().then(this.sourceModelManager.getSearchPath.bind(this.sourceModelManager)).then(function (searchPath) {
  470. /**
  471. * We need to find the dashboard information needed for auditing.
  472. * dashboardInfo object containing optional information used to create headers in dss requestedVersion
  473. * @param dashboardInfo.isDirty - if the dashboard is currently in an unsaved state
  474. * @param dashboardInfo.dashboardId - dashboard store id
  475. * @param dashboardInfo.searchPath - dashboard search path.
  476. */
  477. var dashboardInfo = _this4.dashboardApi.getDashboardInfo();
  478. return new Promise(function (resolve, reject) {
  479. try {
  480. getData(querySpec, 'rawResponse', _this4._queryTypeParam, _this4._getDashboardOptions(dashboardInfo, searchPath, options), options.qfb, options.qfbMode).then(resolve).catch(function (error) {
  481. // Workaround since MUI changes the glass error response. Reset this property so our error handling
  482. // code will still work
  483. error.responseJSON = error ? error.data : {};
  484. if ((typeof querySpec === 'undefined' ? 'undefined' : _typeof(querySpec)) === 'object') {
  485. error.querySpec = querySpec;
  486. } else if (querySpec) {
  487. try {
  488. // Keep track of the query spec that caused the http error
  489. error.querySpec = JSON.parse(querySpec);
  490. } catch (e) {
  491. _this4.logger.error(e);
  492. }
  493. }
  494. reject(error);
  495. });
  496. } catch (error) {
  497. reject(error);
  498. }
  499. });
  500. });
  501. };
  502. ShapingModelManager.prototype.moduleEventListener = function moduleEventListener(notification) {
  503. if (this._ignoreMoserEvents) {
  504. return;
  505. }
  506. switch (notification.getType()) {
  507. case MoserJS.default.Notification.ADD:
  508. //when undo removing of a calculated member, and calculated member is referenced in a widget, the widget will be refershed
  509. if (notification.getFeatureID() === MoserJS.default.Features.FILTER || notification.getFeatureID() === MoserJS.default.Features.ITEM || notification.getFeatureID() === MoserJS.default.Features.CALCULATION) {
  510. //moser throws a lot of events for a single action, so we only want to notify at the end of thread execution
  511. if (!this._hasDebounceNotify) {
  512. this._debounceShapingNotification = this._createDebouncedShapingNotification();
  513. }
  514. this._debounceShapingNotification({
  515. notification: notification,
  516. action: 'ADD'
  517. });
  518. }
  519. break;
  520. case MoserJS.default.Notification.SET:
  521. case MoserJS.default.Notification.REMOVE:
  522. {
  523. this.clearCache();
  524. if (!this._hasDebounceNotify) {
  525. var featureID = notification.getFeatureID();
  526. if (featureID === MoserJS.default.Features.TYPE || featureID === MoserJS.default.Features.SEARCH_PATH || featureID === MoserJS.default.Features.STORE_ID || this._isMembersFolderExpansionNotification(notification)) {
  527. return;
  528. }
  529. this._debounceShapingNotification = this._createDebouncedShapingNotification();
  530. }
  531. var action = notification.getType() === MoserJS.default.Notification.SET ? 'SET' : 'REMOVE';
  532. this._debounceShapingNotification({
  533. noRefresh: this._checkNotificationProperties(notification, 'propertySetByUser_label'),
  534. notification: notification,
  535. action: action
  536. });
  537. }
  538. break;
  539. default:
  540. break;
  541. }
  542. };
  543. //check if propertyName is present in notification passed in
  544. ShapingModelManager.prototype._checkNotificationProperties = function _checkNotificationProperties(notification, propertyName) {
  545. var notifier = typeof notification.getNotifier === 'function' && notification.getNotifier();
  546. if (notifier && notifier.property) {
  547. //return true if specific property name is present in property array
  548. return _.some(notifier.getProperty(), function (prop) {
  549. return typeof prop.getName === 'function' && prop.getName() === propertyName;
  550. });
  551. } else {
  552. return notifier && typeof notifier.getName === 'function' && notifier.getName() === propertyName;
  553. }
  554. };
  555. // When the folder "Members" is expanded for the first time (lazy loaded), MUI sends a notification to tell "memberHidden" is changed from value "true" to "false".
  556. // We should not react to this notification (and more importantly should not trigger shaping model change event) as it has no impact to dashbboard.
  557. // This method checks if the notification is generated from this action to ignore it.
  558. ShapingModelManager.prototype._isMembersFolderExpansionNotification = function _isMembersFolderExpansionNotification(notification) {
  559. if (notification.getNotifier && notification.getNotifier() && notification.getNotifier().name === 'memberHidden') {
  560. if (notification.oldValue === 'true' && notification.newValue === 'false') {
  561. return true;
  562. }
  563. }
  564. return false;
  565. };
  566. ShapingModelManager.prototype._createDebouncedShapingNotification = function _createDebouncedShapingNotification() {
  567. var _this5 = this;
  568. this._hasDebounceNotify = true;
  569. return _.debounce(function (payload) {
  570. payload = payload || {};
  571. payload.sender = {};
  572. var timeStamp = new Date().toISOString();
  573. _this5.sourceModelManager.modificationTime = timeStamp;
  574. _this5.sourceModel.set({
  575. modificationTime: timeStamp
  576. }, {
  577. silent: true
  578. });
  579. if (payload.notification.newValue || payload.notification.oldValue) {
  580. payload.sourceId = _this5.sourceModel.id;
  581. var jsonParseString = function jsonParseString(value) {
  582. if (typeof value === 'string') {
  583. // This might be a JSON string or just a string - we have no way of knowing.
  584. try {
  585. return JSON.parse(value.trim());
  586. } catch (e) {
  587. return value;
  588. }
  589. }
  590. return value;
  591. };
  592. payload.newValue = jsonParseString(payload.notification.newValue);
  593. payload.oldValue = jsonParseString(payload.notification.oldValue);
  594. }
  595. _this5.shapingModel.trigger('shapingmodel:changed', payload);
  596. _this5._hasDebounceNotify = false;
  597. if (_this5._unitTestResolve) {
  598. _this5._unitTestResolve();
  599. }
  600. }, 0);
  601. };
  602. ShapingModelManager.prototype.getModuleId = function getModuleId() {
  603. return this._shapingHelper.context.store.moduleId;
  604. };
  605. /**
  606. * Returns a MetadataColumn object
  607. * @param {String} id The Moser object idForExpression for which to get the metadata for
  608. * @param {boolean} logNotFound - boolean to log error in console when moser object not found or not
  609. * @return {MetadataColumn} Object that has APIs to get at the column metadata
  610. */
  611. ShapingModelManager.prototype.getMetadataColumn = function getMetadataColumn(id) {
  612. var logNotFound = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  613. if (id && id.indexOf(SlotAPIHelper.MULTI_MEASURES_SERIES) !== 0) {
  614. if (!this.metadataColumnCache[id]) {
  615. var moserObject = this.getMoserObject(id);
  616. if (!moserObject) {
  617. if (logNotFound && this.logger) {
  618. this.logger.error('Could not find moser object with id ' + id, this);
  619. }
  620. return null;
  621. }
  622. this.metadataColumnCache[id] = new MetadataColumn({
  623. moserObject: moserObject,
  624. sourceId: this.sourceModel.id,
  625. queryService: this.queryService
  626. });
  627. }
  628. } else {
  629. return null;
  630. }
  631. return this.metadataColumnCache[id];
  632. };
  633. /**
  634. * Called by the shaping moserVisitor. Mostly broken out into its own funciton for easy unit test
  635. * @param {[MetadataColumns]} metadataColumns The resulting array of metadata columns
  636. * @param {Function} Callback to call with each metadataColumn. Callback should return true if the item should be part of the resulting array or false otherwise
  637. * @param {Boolean} breakOnFirstMatch default to false. If true will stop the search once the first item is found.
  638. * @param {Obj} moserObj The moser object which we're currently visiting
  639. * @return true if we should continue visiting the child or moserVistor.BREAK if we should stop visiting
  640. */
  641. ShapingModelManager.prototype._moserVisitorCallback = function _moserVisitorCallback(metadataColumns, callback, breakOnFirstMatch, moserObj) {
  642. var isHidden = moserObj.hidden || moserObj.isHidden && moserObj.isHidden();
  643. if (isHidden) {
  644. return false;
  645. }
  646. if (UNWANTED_MOSER_OBJECT_TYPES_FOR_METADATACOLUMNS.indexOf(moserObj.getObjectType()) === -1) {
  647. var id = moserObj.getIdForExpression ? moserObj.getIdForExpression() : null;
  648. if (id) {
  649. var metadataColumn = this.metadataColumnCache[id] || new MetadataColumn({
  650. moserObject: moserObj,
  651. sourceId: this.sourceModel.id,
  652. queryService: this.queryService
  653. });
  654. this._loadedMetadataColumns[id] = metadataColumn;
  655. if (callback(metadataColumn)) {
  656. metadataColumns.push(metadataColumn);
  657. if (breakOnFirstMatch) {
  658. return Shaping.moserVisitor.BREAK;
  659. }
  660. }
  661. }
  662. }
  663. return true;
  664. };
  665. /**
  666. * [description]
  667. * @param {Function} Callback to call with each metadataColumn. Callback should return true if the item should be part of the resulting array or false otherwise
  668. * @param {Boolean} breakOnFirstMatch default to false. If true will stop the search once the first item is found.
  669. * @return {[MetadataColumns]} Array of MetadataColumn objects
  670. */
  671. ShapingModelManager.prototype.getMetadataColumns = function getMetadataColumns(callback, breakOnFirstMatch) {
  672. if (!this.metadataColumns || callback) {
  673. this.metadataColumns = [];
  674. Shaping.moserVisitor(this.module.metadataTreeView, this._moserVisitorCallback.bind(this, this.metadataColumns, callback, breakOnFirstMatch));
  675. }
  676. return this.metadataColumns;
  677. };
  678. /**
  679. * Returns a MoserJS object
  680. * @param {String} id The Moser object idForExpression for which to get the metadata for
  681. * @return a MoserJS object
  682. */
  683. ShapingModelManager.prototype.getMoserObject = function getMoserObject(id) {
  684. return MoserJS.default.ModuleUtils.getMoserObjectByIdForExpression(id, this.module);
  685. };
  686. ShapingModelManager.prototype.clearCache = function clearCache() {
  687. this.metadataColumnCache = {};
  688. };
  689. ShapingModelManager.prototype.relink = function relink(sourceModel) {
  690. var _this6 = this;
  691. var relinkPromise = void 0;
  692. // ignore events fired from MOSER while relink is processiong
  693. this._ignoreMoserEvents = true;
  694. if (!this.module) {
  695. //this happens when open a saved dashboard where its data source had been deleted from CM
  696. if (this.shapingModel.moserJSON) {
  697. this._updateUseSpecInMoserJSON(sourceModel);
  698. relinkPromise = this._shapingHelper.loadFromJSON(this.shapingModel.moserJSON);
  699. } else {
  700. relinkPromise = this._shapingHelper.addSource({
  701. id: sourceModel.get('assetId'),
  702. href: sourceModel.get('searchPath'),
  703. type: MAP_SOURCETYPE_TO_USESPECTYPE[sourceModel.get('type')],
  704. name: sourceModel.get('name')
  705. });
  706. }
  707. } else {
  708. this.clearCache();
  709. relinkPromise = this._shapingHelper.relink({
  710. id: sourceModel.get('assetId'),
  711. href: sourceModel.get('searchPath'),
  712. type: MAP_SOURCETYPE_TO_USESPECTYPE[sourceModel.type]
  713. });
  714. }
  715. return relinkPromise.then(function (module) {
  716. _this6.module = module;
  717. _this6.shapingModel.moserJSON = MoserJS.default.ModuleUtils.prepareModuleForSave(module);
  718. var payload = void 0;
  719. if (sourceModel && SOURCE_TYPES_TO_UPDATE.indexOf(sourceModel.type) !== -1) {
  720. payload = {
  721. sourceId: sourceModel.id,
  722. relinkIdForExpressions: function relinkIdForExpressions() {
  723. var itemIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  724. return MoserJS.default.ModuleUtils.relinkIdForExpressions(null, itemIds, _this6.module);
  725. }
  726. };
  727. }
  728. // After a link any embedded module we have is not longer up to date. Flag it
  729. // as such so it'll get updated during a save operation.
  730. _this6.shapingModel.setEmbeddedModuleUpToDate(false);
  731. return payload;
  732. }).catch(this._reloadMetadataErrorHandler.bind(this)).finally(function () {
  733. _this6._ignoreMoserEvents = null;
  734. });
  735. };
  736. ShapingModelManager.prototype._updateUseSpecInMoserJSON = function _updateUseSpecInMoserJSON(sourceModel) {
  737. var objectFactory = MoserJS.default.createObjectFactory();
  738. var jsonObjectFactory = MoserJS.default.createJSONObjectFactory(objectFactory);
  739. var module = jsonObjectFactory.createModule(this.shapingModel.moserJSON);
  740. var useSpec = module.getUseSpec()[0];
  741. useSpec.setSearchPath(sourceModel.get('searchPath'));
  742. useSpec.setStoreID(sourceModel.get('assetId'));
  743. useSpec.setType(MAP_SOURCETYPE_TO_USESPECTYPE[sourceModel.type]);
  744. this.shapingModel.moserJSON = MoserJS.default.ModuleUtils.prepareModuleForSave(module);
  745. };
  746. ShapingModelManager.prototype.reloadMetadata = function reloadMetadata() {
  747. this.clearCache();
  748. // If we just reloaded the metadata for a dataset that had no data in the first place
  749. // then we need to reload the dashboard so that the module and shapingHelper get initialized correctly
  750. if (this._isMissingDatasetData()) {
  751. return this.dashboardApi.reloadFromJSONSpec(this.dashboardApi.getSpec());
  752. }
  753. return this._shapingHelper.reloadMetadata().then(this._postReloadMetadataHandler.bind(this, null)).catch(this._reloadMetadataErrorHandler.bind(this));
  754. };
  755. ShapingModelManager.prototype.getDrillGroups = function getDrillGroups() {
  756. return this.module.getDrillGroup();
  757. };
  758. ShapingModelManager.prototype._reloadMetadataErrorHandler = function _reloadMetadataErrorHandler(error) {
  759. //always trigger the event as the widget need to the show error
  760. this.shapingModel.trigger('shapingmodel:changed', {
  761. error: error
  762. });
  763. throw error;
  764. };
  765. ShapingModelManager.prototype._postReloadMetadataHandler = function _postReloadMetadataHandler(sourceModel, module) {
  766. //set the module again because it might be a new one if there is: a session timeout
  767. this.module = module;
  768. var payload = {
  769. sender: {}
  770. };
  771. this.shapingModel.trigger('shapingmodel:changed', payload);
  772. };
  773. ShapingModelManager.prototype.getDrillGroupsForColumn = function getDrillGroupsForColumn(columnId) {
  774. var drillGroups = this.getDrillGroups();
  775. if (!drillGroups || drillGroups.length === 0) {
  776. return null;
  777. }
  778. return drillGroups.filter(this._containsSelectedColumn.bind(this, columnId));
  779. };
  780. ShapingModelManager.prototype._containsSelectedColumn = function _containsSelectedColumn(sourceColumnId, drillGroup) {
  781. return this._findIndex(drillGroup.getSegment(), 'ref', sourceColumnId) !== -1;
  782. };
  783. /*
  784. * Returns the index at which the object with specified property value is found in the array
  785. */
  786. ShapingModelManager.prototype._findIndex = function _findIndex(arrayObjects, sProperty, sValue) {
  787. if (!arrayObjects) {
  788. return -1;
  789. }
  790. for (var idx = 0; idx < arrayObjects.length; idx++) {
  791. if (arrayObjects[idx][sProperty] === sValue) {
  792. return idx;
  793. }
  794. }
  795. return -1;
  796. };
  797. ShapingModelManager.prototype.addCalculation = function addCalculation(properties) {
  798. var calculationExpression = this._shapingHelper.expressionCreateObjectSimpleCalculation(properties, null);
  799. calculationExpression.setRegularAggregate(MoserJS.default.RegularAggregateType.CALCULATED);
  800. return this._shapingHelper.expressionAddToModuleWithValidation(calculationExpression).then(function () {
  801. return calculationExpression.idForExpression;
  802. });
  803. };
  804. /**
  805. * Returns an array of table names for the module
  806. *
  807. * @return {array} An array of table names
  808. */
  809. ShapingModelManager.prototype.getTableNames = function getTableNames() {
  810. var _this7 = this;
  811. if (!this.tableNames) {
  812. this.tableNames = [];
  813. if (this.module) {
  814. var qs = this.module.getQuerySubject();
  815. qs.forEach(function (subject) {
  816. _this7.tableNames.push(subject.getIdentifier());
  817. });
  818. }
  819. }
  820. return this.tableNames;
  821. };
  822. /**
  823. * Return a bolean indicating whether 2 tables have a join relationship or not
  824. *
  825. * @param {string} firstTable - the name of the first table to verify
  826. * @param {string} secondTable - the name of the table to verify against firstTable
  827. */
  828. ShapingModelManager.prototype.tablesHaveJointRelationship = function tablesHaveJointRelationship(firstTable, secondTable) {
  829. if (firstTable && firstTable.length && secondTable && secondTable.length) {
  830. if (firstTable === secondTable) {
  831. //If both names are identical return true if the name exists for the module
  832. var tables = this.getTableNames();
  833. return tables.indexOf(firstTable) !== -1;
  834. } else {
  835. var tableRelationships = this._getRelationships();
  836. if (tableRelationships) {
  837. var isMatched = function isMatched(relationship, value, isLeft) {
  838. return isLeft ? relationship.left === value : relationship.right === value;
  839. };
  840. var jointRelationship = _.find(tableRelationships, function (relationship) {
  841. return isMatched(relationship, firstTable, true) && isMatched(relationship, secondTable, false) || isMatched(relationship, firstTable, false) && isMatched(relationship, secondTable, true);
  842. });
  843. return jointRelationship ? true : false;
  844. }
  845. }
  846. }
  847. };
  848. ShapingModelManager.prototype._getRelationships = function _getRelationships() {
  849. var _this8 = this;
  850. if (!this.tableRelationships) {
  851. var existMap = {};
  852. this.tableRelationships = [];
  853. var relationships = this.module.getRelationship();
  854. if (relationships && relationships.length) {
  855. relationships.forEach(function (relationship) {
  856. var left = relationship.getLeft();
  857. var right = relationship.getRight();
  858. if (left && right) {
  859. var leftRef = left.getRef();
  860. var rightRef = right.getRef();
  861. if (leftRef && rightRef) {
  862. var key = leftRef + rightRef;
  863. if (!existMap[key]) {
  864. existMap[key] = true;
  865. _this8.tableRelationships.push({
  866. left: leftRef,
  867. right: rightRef
  868. });
  869. }
  870. }
  871. }
  872. });
  873. }
  874. }
  875. return this.tableRelationships;
  876. };
  877. /**
  878. * Returns the temp module JSON
  879. */
  880. ShapingModelManager.prototype.getTemporarySessionModuleJSON = function getTemporarySessionModuleJSON() {
  881. return this.module.toJSON();
  882. };
  883. /**
  884. *
  885. * Returns the JSON of a partial copy of the current Moser module containing only the query subjects for the given
  886. * columnIds and/or calculation. This is required for Smarts alternate vis recommender.
  887. *
  888. * @param {array} An array of idForExpressions of columns
  889. */
  890. ShapingModelManager.prototype.copyPartialMoserModuleJSON = function copyPartialMoserModuleJSON(columnIds) {
  891. var _this9 = this;
  892. var factory = MoserJS.default.createObjectFactory();
  893. var newModule = MoserJS.default.ModuleUtils.createModule(factory, this.module.getLabel(), this.module.getExpressionLocale());
  894. var relationships = this.module.getRelationship();
  895. if (relationships.length) {
  896. relationships.forEach(function (relationship) {
  897. return newModule.addRelationship(relationship.clone());
  898. });
  899. }
  900. var querySubjects = [];
  901. var querySubjectInNewModule = function querySubjectInNewModule(querySubject) {
  902. return querySubjects.indexOf(querySubject.getIdentifier()) !== -1;
  903. };
  904. var isCalculation = function isCalculation(column) {
  905. return column.getObjectType() === MoserJS['default'].MoserObjectTypes.CALCULATION;
  906. };
  907. columnIds.forEach(function (columnId) {
  908. var metadataColumn = _this9.getMetadataColumn(columnId);
  909. if (metadataColumn) {
  910. var querySubject = metadataColumn.getTable();
  911. if (querySubject && !querySubjectInNewModule(querySubject)) {
  912. var clonedQS = querySubject.clone();
  913. querySubjects.push(querySubject.getIdentifier());
  914. MoserJS.default.ModuleUtils.removeMembers(clonedQS); //TODO: Temporary - see https://jsw.ibm.com/browse/CADBLW-665
  915. newModule.addQuerySubject(clonedQS);
  916. } else if (!querySubject && isCalculation(metadataColumn)) {
  917. newModule.addCalculation(metadataColumn.getMoserObject().clone());
  918. } else if (!querySubject && !isCalculation(metadataColumn)) {
  919. _this9.logger.error('Unable to add ' + columnId + ' to the module', _this9);
  920. }
  921. }
  922. });
  923. return newModule.toJSON();
  924. };
  925. ShapingModelManager.prototype._getWidgetSubsetIds = function _getWidgetSubsetIds(spec) {
  926. var _this10 = this;
  927. var itemIds = [];
  928. var widgetSpecs = _.flatten([spec.layout ? DashboardSpecUtil.getWidgetModelList(spec.layout) : [], spec.widgets ? _.values(spec.widgets) : [] //Legacy (pre-11.1.7) specs
  929. ]);
  930. _.each(widgetSpecs, function (widget) {
  931. // collect all data items of widget
  932. var dataItems = _.chain(widget.data).compact().flatten()
  933. // only those using the current source
  934. .filter(function (view) {
  935. return view.modelRef === _this10.sourceModel.id;
  936. }).pluck('dataItems').compact().flatten().filter(function (dataItem) {
  937. return dataItem.itemId !== SlotAPIHelper.MULTI_MEASURES_SERIES;
  938. }).value();
  939. if (dataItems.length > 0) {
  940. // include itemId,seletion context itemId, and customGroup itemId among dataItems
  941. itemIds.push.apply(itemIds, _.pluck(dataItems, 'itemId'));
  942. itemIds.push.apply(itemIds, _.chain(dataItems).pluck('selection').compact().flatten().pluck('context').compact().flatten().pluck('itemId').value());
  943. itemIds.push.apply(itemIds, _.chain(dataItems).pluck('originalCustomGroupItemId').compact().flatten().value());
  944. // include local filter columnIds
  945. if (widget.localFilters) {
  946. itemIds.push.apply(itemIds, _this10._getWidgetLocalFilterSubsetIds(widget.localFilters));
  947. }
  948. // include saved prompt columnIds
  949. if (widget.savedPrompts) {
  950. itemIds.push.apply(itemIds, _this10._getWidgetSavedPromptsSubsetIds(widget.savedPrompts));
  951. }
  952. }
  953. });
  954. return itemIds;
  955. };
  956. ShapingModelManager.prototype._getWidgetLocalFilterSubsetIds = function _getWidgetLocalFilterSubsetIds(localFilters) {
  957. var _this11 = this;
  958. var ids = [];
  959. _.each(localFilters, function (filter) {
  960. // filter consists with:
  961. // operator, values and columnId (optional)
  962. // when columnId is omitted, the values would contain the compound filter
  963. if ('columnId' in filter) {
  964. ids.push(filter.columnId);
  965. } else if ('operator' in filter) {
  966. // handle the nested filters recurssively
  967. ids.push.apply(ids, _this11._getWidgetLocalFilterSubsetIds(filter.values));
  968. }
  969. });
  970. return ids;
  971. };
  972. ShapingModelManager.prototype._getWidgetSavedPromptsSubsetIds = function _getWidgetSavedPromptsSubsetIds(savedPrompts) {
  973. var ids = [];
  974. for (var key in savedPrompts) {
  975. if (savedPrompts[key]['modelFilterItem']) {
  976. ids.push(savedPrompts[key]['modelFilterItem']);
  977. }
  978. }
  979. return ids;
  980. };
  981. ShapingModelManager.prototype._getPageContextSubsetIds = function _getPageContextSubsetIds(spec) {
  982. var _this12 = this;
  983. return _.chain(spec.pageContext)
  984. // only those using the current source
  985. .filter(function (pageContext) {
  986. return pageContext.sourceId === _this12.sourceModel.id;
  987. })
  988. // for each page context hierarchyUniqueNames
  989. .pluck('hierarchyUniqueNames').compact().flatten().value();
  990. };
  991. ShapingModelManager.prototype.getMetadataSubsetIds = function getMetadataSubsetIds() {
  992. // no need for optimization on authoring mode
  993. // OR temporary module is already loaded
  994. // OR dealing with inline module (DaaS)
  995. if (this.dashboardApi.getMode() === 'authoring' || this.tempModuleLoaded || this.sourceModel.hasInlineModule()) {
  996. return;
  997. }
  998. if (!this.metatdataSubsetIds) {
  999. var boardModel = this.dashboardApi.getFeature('internal').getBoardModel();
  1000. var dataSourcesMoser = this.dashboardApi.getFeature('DataSources.moser');
  1001. // During upgrade we won't have a boardModel yet.
  1002. if (boardModel) {
  1003. var spec = this.dashboardApi.getFeature('Serializer').toJSON();
  1004. // during the upgrade the spec is expected to be undefined
  1005. if (spec) {
  1006. // merge the widget and page context subset item ids
  1007. // @todo the first two should register as metadataSubsetIds providers
  1008. // so these direct dependancies are eliminated
  1009. var itemIds = [];
  1010. itemIds.push.apply(itemIds, this._getWidgetSubsetIds(spec));
  1011. itemIds.push.apply(itemIds, this._getPageContextSubsetIds(spec));
  1012. itemIds.push.apply(itemIds, dataSourcesMoser.getMetadataSubsetIds());
  1013. this.metatdataSubsetIds = _.uniq(itemIds);
  1014. }
  1015. }
  1016. }
  1017. return this.metatdataSubsetIds;
  1018. };
  1019. return ShapingModelManager;
  1020. }();
  1021. return ShapingModelManager;
  1022. });
  1023. //# sourceMappingURL=ShapingModelManager.js.map