PromptManager.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. *
  5. * (C) Copyright IBM Corp. 2016, 2018
  6. *
  7. * US Government Users Restricted Rights - Use, duplication or disclosure
  8. * restricted by GSA ADP Schedule Contract with IBM Corp.
  9. */
  10. define(['../lib/@waca/core-client/js/core-client/ui/core/Class', '../DynamicFileLoader', '../lib/@waca/core-client/js/core-client/nls/StringResources', '../lib/@waca/core-client/js/core-client/utils/Deferred', 'underscore'], function (BaseClass, DynamicFileLoader, StringResources, Deferred, _) {
  11. 'use strict';
  12. var PromptManager = BaseClass.extend({
  13. SEARCH_AND_SELECT: 'searchAndSelect',
  14. INPUT_BOX: 'inputBox',
  15. CALENDAR: 'calendar',
  16. RANGE: 'range',
  17. CANCEL: 'cancelPromptDialog',
  18. promptModules: {
  19. searchAndSelect: 'dashboard-analytics/prompts/controls/SearchAndSelectPromptView',
  20. inputBox: 'dashboard-analytics/prompts/controls/InputPromptView',
  21. calendar: 'dashboard-analytics/prompts/controls/DateTimePromptView',
  22. range: 'dashboard-analytics/prompts/controls/RangePromptView'
  23. },
  24. /**
  25. * @classdesc Prompt manager class to handle prompt related gestures.
  26. * @constructs
  27. * @public
  28. *
  29. */
  30. init: function init() {
  31. PromptManager.inherited('init', this, arguments);
  32. this._inProgressMap = {};
  33. this._pendingPrompts = [];
  34. }
  35. });
  36. /**
  37. * @public
  38. * @return true if the response status is prompt fault.
  39. */
  40. PromptManager.prototype.isPromptFault = function (data) {
  41. if (!data.responseJSON || !data.responseJSON.errors || !data.responseJSON.errors.length) {
  42. return false;
  43. }
  44. return data.status === 412;
  45. };
  46. /**
  47. * @public api to open prompt view.
  48. *
  49. * @param {Object} options - Mandatory: set of parameters to open the prompt.
  50. @param {Object} options.promptSpec - Mandatory: the prompt info used to open the prompt dialog.
  51. * @param {Object} options.logger - mandatory: the logger.
  52. * @param {Object} options.preferences - mandatory: preferences.
  53. * @param {String} options.packageType - mandatory: the package type, e.g. module, package etc.
  54. * @param {String} options.id - mandatory: the package store id.
  55. * @param {Object} options.whenSingleItemQueryReady - Optional: the callback function to get distinct prompt values.
  56. * @param {Object} options.whenColumnsMinMaxQueryReady - Optional: the callback function to get min / max range prompt values.
  57. * @example
  58. * openPromptView({
  59. * promptSpec: this._getPromptInfo(data),
  60. * logger: logger,
  61. * preferences: preferences,
  62. * packageType: this.dataSetDefinition.type,
  63. * id: this.id,
  64. * whenColumnsMinMaxQueryReady: this.whenColumnsMinMaxQueryReady.bind(this),
  65. * whenSingleItemQueryReady: this.whenSingleItemQueryReady.bind(this),
  66. * getPromptSpec: this._getPromptSpec.bind(this)
  67. * });
  68. *
  69. * Promopt Info:
  70. * {
  71. * "promptType": "",
  72. * "modulePath": null,
  73. * "capabilities": {
  74. * "multivalued": true,
  75. * "optional": false,
  76. * "discreteValue": true
  77. * },
  78. * "modelFilterItem": "[great_outdoors_warehouse_DB2].[EMP_EMPLOYEE_DIM].[CITY]",
  79. * "dataType": "xsdString",
  80. * "modelPath": null,
  81. * "caption": "",
  82. * "parameterName": "multiPrompt",
  83. * "name": "multiPrompt",
  84. * "errorCode": "QF-888"
  85. * }
  86. *
  87. */
  88. PromptManager.prototype.openPromptView = function (options) {
  89. var _this = this;
  90. var deferred = new Deferred();
  91. this.logger = options.logger;
  92. if (!options.promptSpec || options.promptSpec.length === 0) {
  93. deferred.resolve([]);
  94. return deferred.promise;
  95. }
  96. if (options.promptSpec[0].involvesOlapColumn) {
  97. deferred.reject(new Error('unSupportedPromptType'));
  98. return deferred.promise;
  99. }
  100. switch (options.promptSpec[0].errorCode) {
  101. case 'QF-888':
  102. // The prompt defined in FM or Module.
  103. var dlgOptions;
  104. var aDialogDeferred = []; // Array of promises of prompt dialogs when drag and drop multiple prompt items at the same.
  105. var aResovledPromptNames = this._resovleSavedPrompts(options, aDialogDeferred);
  106. // Remove the resolved prompt spec.
  107. if (aResovledPromptNames.length > 0) {
  108. options.promptSpec = _.filter(options.promptSpec, function (promptInfo) {
  109. return _.indexOf(aResovledPromptNames, promptInfo.name) < 0;
  110. });
  111. }
  112. if (!options.promptSpec || !options.promptSpec.length) {
  113. // All required prompts are resolved.
  114. return this._resolvePromptViews(aDialogDeferred);
  115. } else {
  116. // If prompting is disabled for preview, do not prompting user, just return specific error message and let the upper stack (explore) handle it
  117. if (options.isPreview) {
  118. deferred.reject(new Error('promptingIsDisabled'));
  119. return deferred.promise;
  120. }
  121. // There are unresolved prompt(s). Continue to open prompt dialog.
  122. return this._buildPromptInfo(options).then(function (aPromptInfo) {
  123. _.each(aPromptInfo, function (promptInfo) {
  124. dlgOptions = {};
  125. _.extend(dlgOptions, options);
  126. dlgOptions.promptInfo = promptInfo;
  127. dlgOptions.key = promptInfo.name;
  128. dlgOptions.promptDialogName = 'dashboard-analytics/lib/@waca/core-client/js/core-client/ui/dialogs/GenericViewDialog';
  129. aDialogDeferred.push(_this._showPromptView(dlgOptions));
  130. });
  131. return _this._resolvePromptViews(aDialogDeferred);
  132. });
  133. }
  134. default:
  135. }
  136. return deferred.promise;
  137. };
  138. PromptManager.prototype._showPromptView = function (dlgOptions, dfd) {
  139. var key = dlgOptions.key;
  140. var deferred = this._inProgressMap[key];
  141. if (!deferred) {
  142. deferred = dfd || new Deferred();
  143. // Only continue when the prompt is not canceled yet.
  144. if (!dlgOptions.promptInfo || !dlgOptions.promptInfo.canceled) {
  145. if (this._isPrompting()) {
  146. var pending = this._getPendingPrompt(key);
  147. if (pending) {
  148. deferred = pending.deferred;
  149. } else {
  150. this._pendingPrompts.push({
  151. promptFunction: this._showPromptView.bind(this, dlgOptions, deferred),
  152. id: key,
  153. deferred: deferred
  154. });
  155. }
  156. } else {
  157. this._inProgressMap[key] = deferred;
  158. this._prepareAndOpenPromptDialog(dlgOptions, deferred);
  159. }
  160. }
  161. }
  162. return deferred.promise;
  163. };
  164. PromptManager.prototype._prepareAndOpenPromptDialog = function (dlgOptions, deferred) {
  165. var _this2 = this;
  166. switch (dlgOptions.promptSpec[0].errorCode) {
  167. case 'QF-888':
  168. var label = dlgOptions.promptInfo.caption ? dlgOptions.promptInfo.caption : dlgOptions.promptInfo.name;
  169. dlgOptions.viewTitle = StringResources.get('promptControlTitle', {
  170. paramLabel: label
  171. });
  172. switch (dlgOptions.promptInfo.promptModuleName) {
  173. case this.INPUT_BOX:
  174. dlgOptions.viewOptions = {
  175. label: label
  176. };
  177. break;
  178. case this.CALENDAR:
  179. dlgOptions.viewOptions = {
  180. columnId: dlgOptions.promptInfo.modelFilterItem,
  181. timezone: dlgOptions.preferences.timeZoneID,
  182. enableOk: this.enableOk.bind(this)
  183. };
  184. break;
  185. case this.SEARCH_AND_SELECT:
  186. if (dlgOptions.promptInfo.promptValuesPromise) {
  187. dlgOptions.promptInfo.promptValuesPromise.then(function (responseData) {
  188. var promptValues = _this2._getDistinctPromptValuesFromResponse(responseData);
  189. dlgOptions.viewOptions = {
  190. columnId: dlgOptions.promptInfo.modelFilterItem,
  191. promptModuleName: dlgOptions.promptInfo.promptModuleName,
  192. name: dlgOptions.promptInfo.name,
  193. singleSelect: dlgOptions.promptInfo.singleSelect,
  194. viewTitle: dlgOptions.viewTitle,
  195. dataType: dlgOptions.promptInfo.dataType,
  196. promptValues: promptValues,
  197. capabilities: dlgOptions.promptInfo.capabilities,
  198. whenSingleItemQueryReady: dlgOptions.whenSingleItemQueryReady
  199. };
  200. _this2._openPromptDialog(dlgOptions, deferred);
  201. }).catch(function (jqXHR) {
  202. // Glass AjaxService error parameters.
  203. deferred.reject(_.extend(jqXHR, {
  204. name: dlgOptions.promptInfo.name
  205. }));
  206. });
  207. }
  208. break;
  209. case this.RANGE:
  210. dlgOptions.viewOptions = {
  211. columnId: dlgOptions.promptInfo.modelFilterItem,
  212. whenColumnsMinMaxQueryReady: dlgOptions.whenColumnsMinMaxQueryReady,
  213. isMultiPrompt: dlgOptions.promptInfo.isMultiPrompt,
  214. postAutoAggregation: false // dlgOptions.promptInfo.hasOwnProperty('postAutoAggregation') ? dlgOptions.promptInfo.postAutoAggregation : !dlgOptions.promptInfo.isMultiPrompt
  215. };
  216. break;
  217. default:
  218. }
  219. if (!dlgOptions.promptInfo.promptValuesPromise) {
  220. _.extend(dlgOptions.viewOptions, {
  221. viewTitle: dlgOptions.viewTitle,
  222. promptModuleName: dlgOptions.promptInfo.promptModuleName,
  223. name: dlgOptions.promptInfo.name,
  224. dataType: dlgOptions.promptInfo.dataType,
  225. capabilities: dlgOptions.promptInfo.capabilities
  226. });
  227. this._openPromptDialog(dlgOptions, deferred);
  228. }
  229. break;
  230. default:
  231. }
  232. };
  233. PromptManager.prototype._openPromptDialog = function (dlgOptions, deferred) {
  234. var _this3 = this;
  235. DynamicFileLoader.load([this.promptModules[dlgOptions.promptInfo.promptModuleName]]).then(function (viewModule) {
  236. dlgOptions.dlgClassOptions = dlgOptions.dlgClassOptions || {
  237. 'buttons': [{
  238. 'text': StringResources.get('ok'),
  239. 'handler': _this3.onOk.bind(_this3, dlgOptions, deferred),
  240. 'type': 'primary',
  241. 'defaultId': 'ok_button'
  242. }, {
  243. 'text': StringResources.get('cancel'),
  244. 'handler': _this3.onCancel.bind(_this3, dlgOptions, deferred),
  245. 'type': 'secondary',
  246. 'defaultId': 'cancel_button'
  247. }],
  248. 'title': dlgOptions.viewOptions.viewTitle,
  249. 'titleAriaLabel': StringResources.get('promptDialogTitle', {
  250. paramLabel: dlgOptions.label
  251. }),
  252. 'viewClass': viewModule[0],
  253. 'viewOptions': dlgOptions.viewOptions
  254. };
  255. var currentSpec = _.find(dlgOptions.promptSpec, function (promptSpec) {
  256. return dlgOptions.key === promptSpec.name;
  257. });
  258. if (currentSpec && currentSpec.values) {
  259. dlgOptions.viewOptions.defaultValues = currentSpec.values;
  260. }
  261. dlgOptions.viewOptions.enableOk = _this3.enableOk.bind(_this3);
  262. return _this3._openDialog(dlgOptions);
  263. }).then(function () {
  264. if (_this3.promptDialog.viewOptions.promptModuleName !== _this3.RANGE && _this3.promptDialog.viewOptions.promptModuleName !== _this3.CALENDAR) {
  265. _this3.promptDialog.disableOk();
  266. }
  267. }).catch(function () {
  268. if (_this3.logger) {
  269. _this3.logger.error('Could not open prompt dialog', dlgOptions);
  270. }
  271. });
  272. };
  273. PromptManager.prototype._openDialog = function (dlgOptions) {
  274. var _this4 = this;
  275. var deferred = new Deferred();
  276. DynamicFileLoader.load([dlgOptions.promptDialogName]).then(function (modules) {
  277. var PromptDialog = modules[0];
  278. var pDialog = new PromptDialog(dlgOptions.dlgClassOptions);
  279. _this4.promptDialog = pDialog;
  280. pDialog.open();
  281. deferred.resolve();
  282. });
  283. return deferred.promise;
  284. };
  285. PromptManager.prototype.onOk = function (options, deferred) {
  286. this._removeInProgress(options.key);
  287. var promptVals;
  288. var aObjPromptVals = [];
  289. if (this.promptDialog.view.getPromptValues) {
  290. promptVals = this.promptDialog.view.getPromptValues();
  291. } else {
  292. promptVals = [];
  293. }
  294. _.each(promptVals, function (value) {
  295. // Normalize the result.
  296. if (!value.d || !value.u) {
  297. value = {
  298. 'd': value,
  299. 'u': value
  300. };
  301. }
  302. aObjPromptVals.push(value);
  303. });
  304. var resolvedPromptSpec = _.find(options.promptSpec, function (promptInfo) {
  305. return promptInfo.name === options.key;
  306. });
  307. if (this.promptDialog.view.getAdditionalOptions) {
  308. _.extend(resolvedPromptSpec, this.promptDialog.view.getAdditionalOptions());
  309. }
  310. this.promptDialog.hide(); // Close the prompt dialog before resolve the result.
  311. deferred.resolve(_.extend(resolvedPromptSpec, {
  312. values: aObjPromptVals
  313. }));
  314. };
  315. PromptManager.prototype.onCancel = function (options, deferred) {
  316. // Cancel one prompt dialog should trigger cancel of all requried prompt actions since
  317. // all of the non-optional prompts are required for the query to run.
  318. _.each(options.promptSpec, function (spec) {
  319. this._cleanPromptCache(spec.name);
  320. spec.canceled = true;
  321. }, this);
  322. this.promptDialog.hide();
  323. deferred.reject(new Error(this.CANCEL));
  324. };
  325. PromptManager.prototype.enableOk = function (bEnabled) {
  326. this.promptDialog.enableOk(bEnabled);
  327. };
  328. PromptManager.prototype._getPendingPrompt = function (id) {
  329. var pending = _.find(this._pendingPrompts, function (prompt) {
  330. return prompt.id === id;
  331. });
  332. return pending;
  333. };
  334. PromptManager.prototype._executeNextPrompt = function () {
  335. if (this._pendingPrompts.length > 0) {
  336. this._pendingPrompts[0].promptFunction();
  337. this._pendingPrompts.splice(0, 1);
  338. }
  339. };
  340. PromptManager.prototype._removeInProgress = function (id) {
  341. delete this._inProgressMap[id];
  342. this._executeNextPrompt();
  343. };
  344. PromptManager.prototype._cleanPromptCache = function (id) {
  345. delete this._inProgressMap[id];
  346. // Cancel all
  347. for (var i = 0; i < this._pendingPrompts.length; i++) {
  348. if (this._pendingPrompts[i].id === id) {
  349. this._pendingPrompts.splice(i, 1);
  350. break;
  351. }
  352. }
  353. };
  354. PromptManager.prototype._isPrompting = function () {
  355. return _.size(this._inProgressMap) > 0;
  356. };
  357. PromptManager.prototype._resovleSavedPrompts = function (options, aDialogDeferred) {
  358. var aResolved = [];
  359. _.each(options.promptSpec, function (promptInfo) {
  360. // Note: when re-prompt, we don't pass getPromptSpec function so the saved prompt values won't be resolved.
  361. // Instead the prompt dialog will be displayed with saved prompt values.
  362. if (options.getPromptSpec) {
  363. var promptSpec;
  364. promptSpec = options.getPromptSpec(promptInfo.name);
  365. if (promptSpec) {
  366. promptSpec.isMultiPrompt = promptInfo.isMultiPrompt;
  367. aDialogDeferred.push(Promise.resolve(promptSpec));
  368. aResolved.push(promptInfo.name);
  369. }
  370. }
  371. });
  372. return aResolved;
  373. };
  374. PromptManager.prototype._buildPromptInfo = function (options) {
  375. var _this5 = this;
  376. var deferred = new Deferred();
  377. var aParamInfo = [];
  378. _.each(options.promptSpec, function (promptInfo) {
  379. // Remove the cancel flag when new prompt or reprompt happens.
  380. delete promptInfo.canceled;
  381. promptInfo.promptModuleName = _this5._getPromptModuleName(promptInfo);
  382. if (!promptInfo.promptModuleName) {
  383. deferred.reject(new Error('unSupportedPromptType'));
  384. return;
  385. }
  386. promptInfo.singleSelect = promptInfo.promptModuleName === _this5.SEARCH_AND_SELECT && !promptInfo.capabilities.multivalued;
  387. promptInfo.columnId = promptInfo.modelFilterItem;
  388. promptInfo.promptValuesPromise = promptInfo.promptModuleName === _this5.SEARCH_AND_SELECT ? options.whenSingleItemQueryReady({
  389. 'column': _.pick(promptInfo, 'columnId'),
  390. 'nativeQuery': true,
  391. 'promptName': options.name,
  392. 'queryHints': {
  393. 'promptValuesQuery': 'true'
  394. }
  395. }) : null;
  396. aParamInfo.push(promptInfo);
  397. });
  398. deferred.resolve(aParamInfo);
  399. return deferred.promise;
  400. };
  401. /*
  402. * The logic to decide what prompt control to show.
  403. *
  404. */
  405. PromptManager.prototype._getPromptModuleName = function (promptInfo) {
  406. var promptModuleName;
  407. switch (promptInfo.dataType) {
  408. case 'xsdDate':
  409. case 'xsdDateTime':
  410. case 'xsdTime':
  411. if (promptInfo.capabilities.multivalued && promptInfo.capabilities.discreteValue && promptInfo.modelFilterItem) {
  412. promptModuleName = this.SEARCH_AND_SELECT;
  413. } else {
  414. promptModuleName = this.CALENDAR;
  415. }
  416. break;
  417. case 'xsdInt':
  418. case 'xsdLong':
  419. case 'xsdShort':
  420. case 'xsdFloat':
  421. case 'xsdDecimal':
  422. case 'xsdDouble':
  423. if (promptInfo.modelFilterItem) {
  424. if (promptInfo.capabilities.multivalued) {
  425. if (promptInfo.capabilities.discreteValue) {
  426. promptModuleName = this.SEARCH_AND_SELECT;
  427. } else {
  428. promptModuleName = this.RANGE;
  429. }
  430. } else {
  431. promptModuleName = this.INPUT_BOX;
  432. }
  433. } else {
  434. promptModuleName = this.INPUT_BOX;
  435. }
  436. break;
  437. case 'xsdString':
  438. if (promptInfo.modelFilterItem) {
  439. promptModuleName = this.SEARCH_AND_SELECT;
  440. } else {
  441. promptModuleName = this.INPUT_BOX;
  442. }
  443. break;
  444. default:
  445. }
  446. return promptModuleName;
  447. };
  448. /*
  449. * @param {Object} responseData - The response data from ajax call.
  450. *
  451. */
  452. PromptManager.prototype._getDistinctPromptValuesFromResponse = function (resultData) {
  453. var resultRowSize = resultData.getDatapointCount();
  454. var aResults = [];
  455. for (var rowIndex = 0; rowIndex < resultRowSize; rowIndex++) {
  456. // Only one column and one tuple part for singleItemQuery result.
  457. aResults.push(resultData.getCellValue(rowIndex, 0)[0]);
  458. }
  459. return aResults;
  460. };
  461. PromptManager.prototype._resolvePromptViews = function (aDialogDeferred) {
  462. var _this6 = this;
  463. return Promise.all(aDialogDeferred).then(function (resolvedPrompts) {
  464. var parameterValues = [];
  465. _.each(resolvedPrompts, function (resolvedPrompt) {
  466. parameterValues = parameterValues.concat(resolvedPrompt);
  467. });
  468. return parameterValues;
  469. }).catch(function (error) {
  470. if (error.reason && error.reason.name) {
  471. _this6._removeInProgress(error.reason.name);
  472. }
  473. throw error; // re-throw
  474. });
  475. };
  476. return new PromptManager();
  477. });
  478. //# sourceMappingURL=PromptManager.js.map