Prompts.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. * Licensed Materials - Property of IBM
  6. *
  7. * (C) Copyright IBM Corp. 2019, 2020
  8. *
  9. * US Government Users Restricted Rights - Use, duplication or disclosure
  10. * restricted by GSA ADP Schedule Contract with IBM Corp.
  11. */
  12. define(['../../../lib/@waca/dashboard-common/dist/core/APIFactory', './api/PromptsAPI', '../../../DynamicFileLoader', '../../../lib/@waca/core-client/js/core-client/nls/StringResources', '../../../widgets/livewidget/nls/StringResources', 'underscore'], function (APIFactory, PromptsAPI, DynamicFileLoader, StringResources, LiveWidgetStringResources, _) {
  13. var Prompts = function () {
  14. /**
  15. * @classdesc Prompt manager class to handle prompt related gestures.
  16. * @constructs
  17. * @public
  18. *
  19. */
  20. function Prompts(options) {
  21. _classCallCheck(this, Prompts);
  22. this.SEARCH_AND_SELECT = 'searchAndSelect';
  23. this.INPUT_BOX = 'inputBox';
  24. this.CALENDAR = 'calendar';
  25. this.RANGE = 'range';
  26. this.CANCEL = 'cancelPromptDialog';
  27. this.UNSUPPORTED_PROMPT_TYPE = 'unSupportedPromptType';
  28. this.PROMPT_PROPERTIES = ['name', 'capabilities', 'dataType', 'modelFilterItem'];
  29. this.SAVED_PROMPT_PROPERTIES = ['name', 'capabilities', 'dataType', 'values', 'modelFilterItem'];
  30. this.promptModules = {
  31. searchAndSelect: 'dashboard-analytics/features/dashboard/prompts/controls/SearchAndSelectPromptView',
  32. inputBox: 'dashboard-analytics/features/dashboard/prompts/controls/InputPromptView',
  33. calendar: 'dashboard-analytics/features/dashboard/prompts/controls/DateTimePromptView',
  34. range: 'dashboard-analytics/features/dashboard/prompts/controls/RangePromptView' };
  35. this.dashboard = options.dashboardApi;
  36. this.logger = this.dashboard.getGlassCoreSvc('.Logger');
  37. // In progress prompt view
  38. this._inProgressMap = {};
  39. // Pending prompt cache
  40. this._pendingPrompts = [];
  41. var preferences = this.dashboard.getGlassCoreSvc('.UserProfile').preferences;
  42. this._timeZone = preferences.timeZoneID || 'America/New_York';
  43. this._locale = preferences.productLocale || 'en';
  44. }
  45. Prompts.prototype.destroy = function destroy() {
  46. if (this.promptDialog) {
  47. this.promptDialog.destroy();
  48. this.promptDialog = null;
  49. }
  50. this.dashboard = null;
  51. this._pendingPrompts = null;
  52. this._timeZone = null;
  53. this._locale = null;
  54. };
  55. Prompts.prototype.getAPI = function getAPI() {
  56. if (!this.api) {
  57. this.api = APIFactory.createAPI(this, [PromptsAPI]);
  58. }
  59. return this.api;
  60. };
  61. /**
  62. * @public
  63. * @return true if the response status is prompt fault.
  64. */
  65. Prompts.prototype.isPromptFault = function isPromptFault(errorResponse) {
  66. return errorResponse.status === 412 && errorResponse.responseJSON && errorResponse.responseJSON.errors && errorResponse.responseJSON.errors.length;
  67. };
  68. Prompts.prototype.openPromptView = function openPromptView(specList, visualization) {
  69. var _this = this;
  70. if (!specList || !specList.length) {
  71. this.logger.error('Invalid prompt spec');
  72. return Promise.reject();
  73. }
  74. var dataSource = this._getDataSource(specList, visualization);
  75. if (!dataSource) {
  76. // Couldn't find right data source. Throw unsupported error.
  77. this.logger.error('Cannon find related data source for selected prompts');
  78. return this._onUnSupportedPrompt();
  79. }
  80. var infoList = this._buildPromptValueList(specList, dataSource.getId(), visualization);
  81. // If the visualization is preview and there are still unresolved prompts, reject here, Explore will display static view for this preview
  82. if (infoList.length > 0 && visualization.isPreview()) {
  83. var hasUnresolvedPrompt = infoList.some(function (info) {
  84. return !info.values;
  85. });
  86. if (hasUnresolvedPrompt) {
  87. return Promise.reject(new Error('promptingIsDisabled'));
  88. }
  89. }
  90. // Array of prompt promises when drag and drop multiple prompt items at the same.
  91. var valuePromiseList = _.pluck(infoList, 'promptValuesPromise');
  92. var specNameList = _.pluck(infoList, 'name');
  93. return Promise.all(valuePromiseList).then(function (resultList) {
  94. var promptViewPromiseList = [];
  95. resultList.forEach(function (result, index) {
  96. var dlgOptions = {};
  97. dlgOptions.info = infoList[index];
  98. dlgOptions.queryResult = result; // could be QueryResultAPI or undefined depends on what type of prompt is
  99. dlgOptions.dialogName = 'dashboard-analytics/lib/@waca/core-client/js/core-client/ui/dialogs/GenericViewDialog';
  100. if (infoList[index].showView) {
  101. promptViewPromiseList.push(_this._showPromptView(dlgOptions, specNameList, dataSource.getId()));
  102. } else {
  103. promptViewPromiseList.push(Promise.resolve(infoList[index]));
  104. }
  105. });
  106. return Promise.all(promptViewPromiseList).then(function (resolvedPrompts) {
  107. var parameterValues = [];
  108. _.each(resolvedPrompts, function (resolvedPrompt) {
  109. parameterValues.push(resolvedPrompt);
  110. });
  111. return parameterValues;
  112. }).catch(function (error) {
  113. if (error && error.name) {
  114. _this._removeInProgressAndExecuteNext(error.name);
  115. }
  116. throw error; // re-throw
  117. });
  118. }).catch(function (jqXHR) {
  119. // Possible query Error. Need to clean local cache.
  120. var keys = _.keys(_this._inProgressMap);
  121. if (keys && keys.length) {
  122. // There is only one entry in cache
  123. _this._removeInProgressAndExecuteNext(keys[0]);
  124. }
  125. throw jqXHR;
  126. });
  127. };
  128. Prompts.prototype.getPromptSpecList = function getPromptSpecList(errorResponse) {
  129. var error = errorResponse.responseJSON.errors[0];
  130. if (!error || !error.code) {
  131. return;
  132. }
  133. var params = error.parameters;
  134. if (!params || params.length === 0) {
  135. return;
  136. }
  137. var paramInfoList = _.map(params, function (param) {
  138. param.value.name = param.name;
  139. return param.value;
  140. });
  141. return paramInfoList;
  142. };
  143. Prompts.prototype._getDataSource = function _getDataSource(specList, visualization) {
  144. var dataSource = null;
  145. if (visualization) {
  146. dataSource = visualization.getDataSource();
  147. } else {
  148. var modelFilterItem = _.find(specList, function (spec) {
  149. return spec.modelFilterItem;
  150. });
  151. if (modelFilterItem) {
  152. var dataSources = this.dashboard.getFeature('DataSources').getDataSourceList();
  153. dataSource = dataSources.forEach(function (dataSource) {
  154. return dataSource.getMetadataColumn(modelFilterItem);
  155. });
  156. }
  157. }
  158. return dataSource;
  159. };
  160. // TODO remove onCancelPromptDialog and onUnSupportedPrompt from DeprecatedWidgetBase once
  161. // new API is live
  162. Prompts.prototype._onCancelPromptDialog = function _onCancelPromptDialog() {
  163. this.dashboard.undo();
  164. };
  165. Prompts.prototype._onUnSupportedPrompt = function _onUnSupportedPrompt() {
  166. var _this2 = this;
  167. DynamicFileLoader.load(['dashboard-analytics/lib/@waca/core-client/js/core-client/ui/dialogs/ConfirmationDialog']).then(function (modules) {
  168. var dlg = new modules[0]('warning', '', LiveWidgetStringResources.get(_this2.UNSUPPORTED_PROMPT_TYPE));
  169. dlg.confirm(_this2._onCancelPromptDialog.bind(_this2), _this2._onCancelPromptDialog.bind(_this2));
  170. });
  171. return Promise.reject(new Error(this.UNSUPPORTED_PROMPT_TYPE));
  172. };
  173. Prompts.prototype._buildPromptValueList = function _buildPromptValueList(specList, dataSourceId, visualization) {
  174. var _this3 = this;
  175. var infoList = [];
  176. var savedPrompts = visualization && visualization.getSavedPrompts();
  177. _.each(specList, function (spec) {
  178. var savedSpec = savedPrompts && savedPrompts.getPromptSpec(spec.name);
  179. var info = void 0;
  180. if (savedSpec) {
  181. info = _.pick(savedSpec, _this3.SAVED_PROMPT_PROPERTIES);
  182. if (spec.capabilities.optional && info.values[0] && info.values[0].mun) {
  183. info.values[0] = info.values[0].mun;
  184. info.showView = false;
  185. } else if (spec.rePrompt) {
  186. // Flag used to differentiate re-prompt or re-open saved dashboard.
  187. // Saved prompts should not be re-prompted unless click on edit prompt button.
  188. // showView is undefined by default.
  189. info.showView = true;
  190. info.rePrompt = true;
  191. _this3._initViewInfo(dataSourceId, info, spec);
  192. }
  193. } else {
  194. info = _.pick(spec, _this3.PROMPT_PROPERTIES);
  195. // Add optional prompt default value and save it
  196. if (spec.capabilities.optional) {
  197. info.values = ['nil'];
  198. info.showView = false;
  199. } else {
  200. info.showView = true;
  201. _this3._initViewInfo(dataSourceId, info, spec);
  202. }
  203. }
  204. if (!info.showView) {
  205. info.promptValuesPromise = Promise.resolve();
  206. }
  207. infoList.push(info);
  208. });
  209. return infoList;
  210. };
  211. Prompts.prototype._initViewInfo = function _initViewInfo(dataSourceId, info, requestSpec) {
  212. info.promptModuleName = this._getPromptModuleName(requestSpec);
  213. info.columnId = info.modelFilterItem;
  214. if (!this.queryService) {
  215. this.queryService = this.dashboard.getFeature('QueryService');
  216. }
  217. switch (info.promptModuleName) {
  218. case this.SEARCH_AND_SELECT:
  219. info.promptValuesPromise = this.queryService.getColumnValues(dataSourceId, info.columnId);
  220. break;
  221. case this.RANGE:
  222. info.promptValuesPromise = this.queryService.getColumnMinMaxValues(dataSourceId, info.columnId);
  223. break;
  224. default:
  225. info.promptValuesPromise = Promise.resolve();
  226. }
  227. };
  228. /*
  229. * Returns the prompt dialog type.
  230. */
  231. Prompts.prototype._getPromptModuleName = function _getPromptModuleName(spec) {
  232. var promptModuleName = void 0;
  233. switch (spec.dataType) {
  234. case 'xsdDate':
  235. case 'xsdDateTime':
  236. case 'xsdTime':
  237. if (spec.capabilities.multivalued && spec.capabilities.discreteValue && spec.modelFilterItem) {
  238. promptModuleName = this.SEARCH_AND_SELECT;
  239. } else {
  240. promptModuleName = this.CALENDAR;
  241. }
  242. break;
  243. case 'xsdInt':
  244. case 'xsdLong':
  245. case 'xsdShort':
  246. case 'xsdFloat':
  247. case 'xsdDecimal':
  248. case 'xsdDouble':
  249. if (spec.modelFilterItem) {
  250. if (spec.capabilities.multivalued) {
  251. if (spec.capabilities.discreteValue) {
  252. promptModuleName = this.SEARCH_AND_SELECT;
  253. } else {
  254. promptModuleName = this.RANGE;
  255. }
  256. } else {
  257. promptModuleName = this.INPUT_BOX;
  258. }
  259. } else {
  260. promptModuleName = this.INPUT_BOX;
  261. }
  262. break;
  263. case 'xsdString':
  264. if (spec.modelFilterItem) {
  265. promptModuleName = this.SEARCH_AND_SELECT;
  266. } else {
  267. promptModuleName = this.INPUT_BOX;
  268. }
  269. break;
  270. default:
  271. promptModuleName = this.INPUT_BOX;
  272. }
  273. return promptModuleName;
  274. };
  275. Prompts.prototype._showPromptView = function _showPromptView(dlgOptions, specNameList, dataSourceId, pendingViewPromise) {
  276. var id = dlgOptions.info.name;
  277. var promptViewPromise = this._inProgressMap[id];
  278. if (!promptViewPromise) {
  279. promptViewPromise = pendingViewPromise || Promise.defer();
  280. dlgOptions.promptViewPromise = promptViewPromise;
  281. if (this._isPrompting()) {
  282. // Pending prompt won't return a promise until its turn. Pending prompt view promise is
  283. // added to local cache in this._executeNextPrompt()
  284. var pendingPrompt = _.find(this._pendingPrompts, function (prompt) {
  285. return prompt.id === id;
  286. });
  287. if (pendingPrompt) {
  288. promptViewPromise = pendingPrompt.pendingViewPromise;
  289. } else {
  290. pendingPrompt = {
  291. id: id,
  292. promptFunction: this._showPromptView.bind(this, dlgOptions, specNameList, dataSourceId, promptViewPromise),
  293. pendingViewPromise: promptViewPromise
  294. };
  295. this._pendingPrompts.push(pendingPrompt);
  296. }
  297. } else {
  298. this._inProgressMap[id] = promptViewPromise;
  299. this._prepareAndOpenDialog(dlgOptions, specNameList, dataSourceId);
  300. }
  301. }
  302. return promptViewPromise.promise;
  303. };
  304. Prompts.prototype._prepareAndOpenDialog = function _prepareAndOpenDialog(dlgOptions, specNameList, dataSourceId) {
  305. var label = dlgOptions.info.caption || dlgOptions.info.name;
  306. dlgOptions.viewTitle = StringResources.get('promptControlTitle', {
  307. paramLabel: label
  308. });
  309. dlgOptions.viewOptions = _.pick(dlgOptions.info, ['promptModuleName', 'name', 'dataType', 'capabilities']);
  310. dlgOptions.viewOptions.viewTitle = dlgOptions.viewTitle;
  311. switch (dlgOptions.info.promptModuleName) {
  312. case this.INPUT_BOX:
  313. dlgOptions.viewOptions.label = label;
  314. break;
  315. case this.CALENDAR:
  316. dlgOptions.viewOptions.columnId = dlgOptions.info.modelFilterItem;
  317. dlgOptions.viewOptions.timezone = this._timeZone;
  318. dlgOptions.viewOptions.locale = this._locale;
  319. break;
  320. case this.SEARCH_AND_SELECT:
  321. {
  322. dlgOptions.viewOptions.columnId = dlgOptions.info.modelFilterItem;
  323. dlgOptions.viewOptions.singleSelect = !dlgOptions.info.capabilities.multivalued;
  324. dlgOptions.viewOptions.queryResult = dlgOptions.queryResult;
  325. dlgOptions.viewOptions.getColumnValues = this.queryService.getColumnValues.bind(this, dataSourceId);
  326. break;
  327. }
  328. case this.RANGE:
  329. {
  330. dlgOptions.viewOptions.columnId = dlgOptions.info.modelFilterItem;
  331. dlgOptions.viewOptions.queryResult = dlgOptions.queryResult;
  332. dlgOptions.viewOptions.getColumnMinMaxValues = this.queryService.getColumnMinMaxValues.bind(this, dataSourceId);
  333. break;
  334. }
  335. default:
  336. }
  337. return this._openPromptDialog(dlgOptions, specNameList);
  338. };
  339. Prompts.prototype._openPromptDialog = function _openPromptDialog(dlgOptions, specNameList) {
  340. var _this4 = this;
  341. return DynamicFileLoader.load([this.promptModules[dlgOptions.info.promptModuleName]]).then(function (viewModule) {
  342. var dlgClassOptions = {
  343. showCloseX: false,
  344. buttons: [{
  345. text: StringResources.get('ok'),
  346. handler: _this4.onOk.bind(_this4, dlgOptions),
  347. type: 'primary',
  348. defaultId: 'ok_button'
  349. }, {
  350. text: StringResources.get('cancel'),
  351. handler: _this4.onCancel.bind(_this4, dlgOptions, specNameList),
  352. type: 'secondary',
  353. defaultId: 'cancel_button'
  354. }],
  355. title: dlgOptions.viewOptions.viewTitle,
  356. titleAriaLabel: StringResources.get('promptDialogTitle', {
  357. paramLabel: dlgOptions.label
  358. }),
  359. viewClass: viewModule[0],
  360. viewOptions: dlgOptions.viewOptions
  361. };
  362. if (dlgOptions.info.values) {
  363. dlgOptions.viewOptions.defaultValues = dlgOptions.info.values;
  364. }
  365. dlgOptions.viewOptions.enableOk = _this4.enableOk.bind(_this4);
  366. return DynamicFileLoader.load([dlgOptions.dialogName]).then(function (modules) {
  367. var PromptDialog = modules[0];
  368. _this4.promptDialog = new PromptDialog(dlgClassOptions);
  369. _this4.promptDialog.open();
  370. });
  371. }).then(function () {
  372. if (_this4.promptDialog.viewOptions.promptModuleName !== _this4.RANGE && _this4.promptDialog.viewOptions.promptModuleName !== _this4.CALENDAR) {
  373. _this4.promptDialog.disableOk();
  374. }
  375. }).catch(function (error) {
  376. _this4.logger.error('Could not open prompt dialog', error);
  377. dlgOptions.promptViewPromise.reject(_.extend(error, {
  378. name: dlgOptions.info.name
  379. }));
  380. });
  381. };
  382. Prompts.prototype.onOk = function onOk(options) {
  383. this._removeInProgressAndExecuteNext(options.info.name);
  384. var promptValueList = void 0;
  385. if (this.promptDialog.view.getPromptValues) {
  386. promptValueList = this.promptDialog.view.getPromptValues();
  387. } else {
  388. promptValueList = [];
  389. }
  390. var resolvedSpec = options.info;
  391. resolvedSpec.values = _.map(promptValueList, function (value) {
  392. if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
  393. return _.pick(value, ['value', 'label']);
  394. } else {
  395. return {
  396. 'value': value,
  397. 'label': value
  398. };
  399. }
  400. });
  401. this.promptDialog.hide();
  402. return options.promptViewPromise.resolve(resolvedSpec);
  403. };
  404. Prompts.prototype.onCancel = function onCancel(options, specNameList) {
  405. // Cancel one prompt dialog should trigger cancel of all requried prompt actions since
  406. // all of the non-optional prompts are required for the query to run.
  407. this._cleanPromptCache(options.info.name, specNameList);
  408. this.promptDialog.hide();
  409. if (!options.info.rePrompt) {
  410. // drag and drop prompts are combined actions need to undo this action.
  411. // E.g. Dnd prompts to local filter, slots, canvas, or tab filter zone triggers multiple
  412. // events. If cancel happened in on Dnd prompts, need to undo the actions.
  413. // But in case of re-prompts, only need to close the prompts dialog.
  414. this._onCancelPromptDialog();
  415. }
  416. var error = new Error(this.CANCEL);
  417. error.name = options.info.name;
  418. return options.promptViewPromise.reject(error);
  419. };
  420. Prompts.prototype.enableOk = function enableOk(bEnabled) {
  421. this.promptDialog.enableOk(bEnabled);
  422. };
  423. Prompts.prototype._isPrompting = function _isPrompting() {
  424. return _.size(this._inProgressMap) > 0;
  425. };
  426. Prompts.prototype._executeNextPrompt = function _executeNextPrompt() {
  427. if (this._pendingPrompts.length > 0) {
  428. var nextPrompt = this._pendingPrompts.shift();
  429. return nextPrompt.promptFunction();
  430. }
  431. };
  432. Prompts.prototype._removeInProgressAndExecuteNext = function _removeInProgressAndExecuteNext(id) {
  433. delete this._inProgressMap[id];
  434. this._executeNextPrompt();
  435. };
  436. Prompts.prototype._cleanPromptCache = function _cleanPromptCache(id, specNameList) {
  437. // Cancel all current requests
  438. this._pendingPrompts = _.filter(this._pendingPrompts, function (pendingPrompt) {
  439. return specNameList.indexOf(pendingPrompt.id) === -1;
  440. });
  441. delete this._inProgressMap[id];
  442. };
  443. return Prompts;
  444. }();
  445. return Prompts;
  446. });
  447. //# sourceMappingURL=Prompts.js.map