VIPRDataRequestHandler.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. /**
  4. *+------------------------------------------------------------------------+
  5. *| Licensed Materials - Property of IBM
  6. *| IBM Cognos Products: Dashboard
  7. *| (C) Copyright IBM Corp. 2017, 2020
  8. *|
  9. *| US Government Users Restricted Rights - Use, duplication or disclosure
  10. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  11. *+------------------------------------------------------------------------+
  12. */
  13. define(['underscore', '../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../dataSources/ModuleUtils', '../../widgets/livewidget/query/QueryFilterSpec', '../../widgets/livewidget/nls/StringResources', './VIPRUtils'], function (_, Class, ModuleUtils, QueryFilterSpec, stringResources, VIPRUtils) {
  14. 'use strict';
  15. // IDataRequestHandler implementation to provide secondary query results to VIPR
  16. var VIPRHandler = Class.extend({
  17. CATALYST_OP: {
  18. KEYDRIVER: 'GetKeyDrivers',
  19. DECISIONTREE: 'GetDecisionTree'
  20. },
  21. // view warning ids
  22. GEO_AMBIGUOUS_ID: 'geo_ambiguous',
  23. GEO_UNMATCHED_ID: 'geo_unmatched',
  24. PREDICT_ERROR_ID: 'predict_error',
  25. PREDICT_WARNING_ID: 'predict_warning',
  26. TILED_MAP_ATLAS: 'tiledMap_atlas',
  27. TYPE_ATLAS: 'atlas',
  28. _lastRequestData: {},
  29. init: function init(options) {
  30. this.logger = options.logger;
  31. this.visAPI = options.visAPI;
  32. this.queryService = this.visAPI.getQueryManager().queryService;
  33. this.visView = options.visView;
  34. this.dashboardApi = this.visAPI.ownerWidget.getDashboardApi();
  35. /*Each request could specify a request complete callback*/
  36. this._callbacks = {
  37. 'Atlas': this._geoQueryCallback.bind(this),
  38. 'MapPolygons': this._geoQueryCallback.bind(this),
  39. 'Catalyst': this._fastPredictCallback.bind(this)
  40. };
  41. this._customData = null;
  42. },
  43. resetCustomData: function resetCustomData() {
  44. this._customData = null;
  45. },
  46. _geoQueryCallback: function _geoQueryCallback(visView, data) {
  47. if (visView && visView.infoIndicator && data) {
  48. var candidates = [{
  49. id: this.GEO_AMBIGUOUS_ID,
  50. property: 'ambiguous',
  51. resourceId: 'geomapAmbiguousLocations'
  52. }, {
  53. id: this.GEO_UNMATCHED_ID,
  54. property: 'unmatched',
  55. resourceId: 'geomapUnrecognizedLocations'
  56. }];
  57. visView.infoIndicator.addInfo(_.map(candidates, function (e) {
  58. return {
  59. id: e.id,
  60. label: stringResources.get(e.resourceId),
  61. items: _.map(data[e.property], function (s) {
  62. return {
  63. id: '_' + s.replace(/\s/g, '_'),
  64. label: s
  65. };
  66. })
  67. };
  68. }));
  69. }
  70. },
  71. _addErrorsAndWarningsForCandidates: function _addErrorsAndWarningsForCandidates(visView, candidates, predictStatus) {
  72. _.each(candidates, function (candidate) {
  73. var properties = predictStatus[candidate.property];
  74. if (properties && properties.length) {
  75. visView.infoIndicator.addInfo([{
  76. id: candidate.id,
  77. label: stringResources.get(candidate.resourceId),
  78. items: _.map(properties, function (prop) {
  79. return {
  80. id: prop.id,
  81. label: prop.caption
  82. };
  83. })
  84. }]);
  85. }
  86. });
  87. },
  88. /**
  89. * FPD requests may result in messages being added that we want
  90. * displayed in the insights indicator dialog. We should check
  91. * for those here and notify needed managers to the changes.
  92. */
  93. _addFPDInsightMessages: function _addFPDInsightMessages(predictStatus) {
  94. // Only log messages if the bundle is configured to accept it.
  95. if (VIPRUtils.doesConfigPropertyMatchExpected(this.visAPI.getVisId(), 'shouldLogFPDInsightMessages', true)) {
  96. var smartsAnnotationManager = this.visAPI.getSmartAnnotationsManger();
  97. // no point doing anything if we have no manager to report to
  98. if (smartsAnnotationManager) {
  99. var messages = null; // Default to no messages
  100. if (predictStatus && predictStatus.messages && predictStatus.messages.length > 0) {
  101. messages = predictStatus.messages;
  102. }
  103. smartsAnnotationManager.addFPDInsightMessages(messages);
  104. this.visView.updateSmartAnnotationsIndicator();
  105. }
  106. }
  107. },
  108. _fastPredictCallback: function _fastPredictCallback(visView, data) {
  109. if (visView && visView.infoIndicator && data && data.predictStatus) {
  110. // check for FPD call back messages and report them if needed
  111. this._addFPDInsightMessages(data.predictStatus);
  112. var predictStatus = data.predictStatus;
  113. var candidates = [{
  114. id: this.PREDICT_ERROR_ID,
  115. property: 'errors',
  116. resourceId: 'predict_errors'
  117. }, {
  118. id: this.PREDICT_WARNING_ID,
  119. property: 'warnings',
  120. resourceId: 'predict_warnings'
  121. }];
  122. this._addErrorsAndWarningsForCandidates(visView, candidates, predictStatus);
  123. }
  124. },
  125. // Implement IDataRequestHandler
  126. request: function request(_request) {
  127. switch (_request.name) {
  128. case 'Schematics':
  129. this._handleSchematicRequest(_request);
  130. break;
  131. case 'Atlas':
  132. case 'MapPolygons':
  133. this._handleGeoRequest(_request);
  134. break;
  135. case 'Catalyst':
  136. this._handleKeyDriversRequest(_request);
  137. break;
  138. default:
  139. this._requestFail(_request);
  140. }
  141. },
  142. /**
  143. * Log a notification from VIPR
  144. * @param {String} level (Severity) The severity of the notification, e.g. "info", "warn" or "error".
  145. * @param {String} message The message of the notification.
  146. * @param {Object} [payload] Optional: payload of the notification, for truncations when data is clipped
  147. */
  148. _logNotification: function _logNotification(level, message, payload) {
  149. var _this = this;
  150. switch (level) {
  151. case 'warn':
  152. {
  153. this.logger.warn(message);
  154. if (payload && payload.truncations) {
  155. payload.truncations.forEach(function (truncation) {
  156. _this.logger.warn(truncation.toString());
  157. });
  158. }
  159. break;
  160. }
  161. case 'error':
  162. {
  163. this.logger.error(message);
  164. if (payload && payload.truncations) {
  165. payload.truncations.forEach(function (truncation) {
  166. _this.logger.error(truncation.toString());
  167. });
  168. }
  169. break;
  170. }
  171. }
  172. },
  173. /**
  174. * Display a notification from VIPR
  175. * @param {String} level (Severity) The severity of the notification, e.g. "info", "warn" or "error".
  176. * @param {String} message The message of the notification.
  177. * @param {Object} [payload] Optional: payload of the notification, for truncations when data is clipped
  178. */
  179. _addNotifyMessagesToInfoIndicator: function _addNotifyMessagesToInfoIndicator(level, message, payload, id, type) {
  180. var _this2 = this;
  181. // If there is a message lets add it to our info indicator.
  182. if (this.visView && this.visView.infoIndicator && message) {
  183. var candidates = [{
  184. id: 'visualization_notifications',
  185. property: 'visualizationnotifications',
  186. resourceId: 'visualizationNotifications'
  187. }];
  188. var items = [];
  189. items.push(this._getMessageLabel(message, false, id, type));
  190. if (payload && payload.truncations) {
  191. payload.truncations.forEach(function (truncation) {
  192. (function (message) {
  193. items.push(this._getMessageLabel(message, true /* isSubMessage */));
  194. }).bind(_this2)(truncation.toString());
  195. });
  196. }
  197. this.visView.infoIndicator.addInfo(_.map(candidates, function (e) {
  198. return {
  199. id: e.id,
  200. label: stringResources.get(e.resourceId),
  201. items: items
  202. };
  203. }));
  204. }
  205. },
  206. /**
  207. * @param {String} messageString - message to add
  208. * @param {Boolean} isSubMessage - is this a sub message of a main message (more clipping info for example)
  209. * @returns {Object} in form needed for the info indicator view
  210. *
  211. */
  212. _getMessageLabel: function _getMessageLabel(messageString, isSubMessage, id, type) {
  213. return {
  214. id: id || '_' + messageString.replace(/\s/g, '_'),
  215. type: type,
  216. label: messageString,
  217. isSubMessage: isSubMessage ? isSubMessage : false
  218. };
  219. },
  220. _isNotificationAGeoWarning: function _isNotificationAGeoWarning(level, id) {
  221. return level === 'warn' && id === this.TILED_MAP_ATLAS;
  222. },
  223. /**
  224. * Handle a notification from VIPR
  225. * @param {String} level (Severity) The severity of the notification, e.g. "info", "warn" or "error".
  226. * @param {String} message The message of the notification.
  227. * @param {Object} [payload] Optional: payload of the notification, for truncations when data is clipped
  228. * @param {String} id - id of the message being returned
  229. */
  230. notify: function notify(_notification) {
  231. this._logNotification(_notification.level, _notification.localizedMessage, _notification.payload);
  232. /* Geo messages are a little different then others. We currently listen to
  233. * the responses coming back from the geo query. We also listen to the notifications
  234. * being sent back from Vida. They both state the same thing so we see them both in the
  235. * Info Indicator dialog. Normally we'd ignore the geo response and listen only
  236. * to Vida, but Vida returns its warnings as a formatted string such as
  237. * 'Ambigous locations: ["Canada"]' which means we'd have to parse it interpret
  238. * it to add it to the info indicator in the way we want. The geo response
  239. * provides it in a key value pair that makes this very easy. So, we will
  240. * listen to the geo response, add it to the info indicator, but in this
  241. * notify message we will only log it and not add it to the info indicator.
  242. */
  243. if (_notification.type === 'clear') {
  244. this._clearIndicatorWarning(_notification);
  245. } else if (!this._isNotificationAGeoWarning(_notification.level, _notification.id)) {
  246. this._addNotifyMessagesToInfoIndicator(_notification.level, _notification.localizedMessage, _notification.payload, _notification.id, _notification.type);
  247. }
  248. },
  249. _clearIndicatorWarning: function _clearIndicatorWarning(notification) {
  250. var _this3 = this;
  251. if (notification.payload && notification.payload.length) {
  252. notification.payload.forEach(function (item) {
  253. if (item.id === _this3.TILED_MAP_ATLAS && item.type === _this3.TYPE_ATLAS) {
  254. _this3.visView.infoIndicator.clearMessagesWithIds([_this3.GEO_AMBIGUOUS_ID, _this3.GEO_UNMATCHED_ID]);
  255. } else {
  256. _this3.visView.infoIndicator.clearMessagesByIdAndSubtype(item);
  257. }
  258. });
  259. }
  260. },
  261. _setDefaultSelections: function _setDefaultSelections() {
  262. var enableSelection = this.dashboardApi.getConfiguration('enableCustomDataSelection') || VIPRUtils.supportsCustomDataSelection(this.visAPI.getVisId());
  263. if (enableSelection) {
  264. var customDataSelections = this.visAPI.getDecoratedCustomData('selected');
  265. var renderingNewData = !!(this.visView && this.visView.getRenderingNewData());
  266. // Default to the first customData to be selected
  267. if (renderingNewData && customDataSelections.length === 0 && this._customData.length > 0) {
  268. var defaultSelection = this._customData[0];
  269. var defaultSelections = [];
  270. var defaultSelectionIds = [];
  271. // For comet: default first value should be grouped with others if they have the same valueFloored
  272. if (defaultSelection.hasOwnProperty('valueFloored')) {
  273. defaultSelections = this._customData.filter(function (data) {
  274. return data.valueFloored === defaultSelection.valueFloored;
  275. });
  276. defaultSelectionIds = defaultSelections.map(function (data) {
  277. return { id: data.id, value: true };
  278. });
  279. } else {
  280. defaultSelections.push(defaultSelection);
  281. defaultSelectionIds.push({ id: defaultSelection.id, value: true });
  282. }
  283. this.visAPI.setCustomDataDecoration('selected', defaultSelectionIds, { silent: true });
  284. customDataSelections.push.apply(customDataSelections, defaultSelections);
  285. }
  286. customDataSelections.forEach(function (item) {
  287. item.setDecoration && item.setDecoration('selected', true);
  288. }.bind(this));
  289. }
  290. },
  291. setCustomData: function setCustomData(type, customData) {
  292. if (!this._customData) {
  293. this._customData = customData;
  294. this._setDefaultSelections();
  295. }
  296. },
  297. getCustomData: function getCustomData() {
  298. return this._customData;
  299. },
  300. _invokeCallback: function _invokeCallback(name, data) {
  301. var fnCallback = this._callbacks[name];
  302. if (fnCallback) {
  303. fnCallback(this.visView, data);
  304. }
  305. },
  306. _requestComplete: function _requestComplete(_request, data) {
  307. var shouldCache = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  308. if (shouldCache) {
  309. this.setLastRequestData(_request.name, data);
  310. }
  311. if (_request.name === 'Catalyst') {
  312. //for catalyst we always cache the raw data, then filter it on demand
  313. //so that predict data can be shared by decision tree and other driver analysis
  314. var operation = this._getCatalystOperation(_request);
  315. var methods = this._getFastPatternDetectionMethods(operation);
  316. data = this._filterPredictResults(data, methods);
  317. }
  318. this._invokeCallback(_request.name, data);
  319. _request.complete(data);
  320. },
  321. _requestFail: function _requestFail(_request, data) {
  322. var shouldCache = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  323. if (shouldCache) {
  324. this.setLastRequestData(_request.name, null);
  325. }
  326. this._invokeCallback(_request.name, data);
  327. _request.fail(data);
  328. },
  329. /**
  330. * Last request data setter
  331. *
  332. * @param {string} key - key to identify the request
  333. * @param {object} data - data to store
  334. */
  335. setLastRequestData: function setLastRequestData(key, data) {
  336. this._lastRequestData[key] = data;
  337. },
  338. /**
  339. * Last request data getter
  340. *
  341. * @param {string} key - key to identify the request
  342. *
  343. * @return {object} previously stored data
  344. */
  345. getLastRequestData: function getLastRequestData(key) {
  346. return this._lastRequestData[key];
  347. },
  348. /**
  349. * Make the rest API call to query for the SVG associated with a schematic
  350. * @param {json object} payload. payload are in the following format:
  351. * payload = { method: "get", library: <schematic-library-id>, type: "meta" }
  352. * payload = { method: "get", library: <schematic-library-id>, name: <schematic-image-source-path> }
  353. * @returns {json object} for first payload it return the content of bi/v1/visualizations/[id]/content/meta.json in json format.
  354. * for second payload it returns content svg in json format.
  355. */
  356. _handleSchematicRequest: function _handleSchematicRequest(_request) {
  357. var _this4 = this;
  358. var getSchematicContent = this.visView.visualization.getDefinition().getProperty('getSchematicContent');
  359. if (getSchematicContent && _request.payload) {
  360. return getSchematicContent(_request.payload, this.dashboardApi).then(function (data) {
  361. _this4._requestComplete(_request, data);
  362. }, function (err) {
  363. _this4._requestFail(_request, err);
  364. });
  365. } else {
  366. this._requestFail(_request, new Error(stringResources.get('schematicServiceRequestError')));
  367. }
  368. },
  369. _getGeoEndpoint: function _getGeoEndpoint() {
  370. var endpoint = this.dashboardApi.getConfiguration('geoService');
  371. // force to use the default endpoint for "CA"
  372. return endpoint === 'CA' ? null : endpoint;
  373. },
  374. _handleGeoRequest: function _handleGeoRequest(_request) {
  375. this.visView.infoIndicator.clearMessagesWithIds([this.GEO_AMBIGUOUS_ID, this.GEO_UNMATCHED_ID]);
  376. this.queryService.runGeoQuery(_request.payload, this._getGeoEndpoint()).then(function (data) {
  377. if (_request.payload.useMapBoxPB) {
  378. Object.defineProperty(data, 'results', Object.getOwnPropertyDescriptor(data, 'mapboxData'));
  379. }
  380. this._requestComplete(_request, data);
  381. }.bind(this), function (err) {
  382. this._requestFail(_request, err);
  383. }.bind(this));
  384. },
  385. _getCatalystOperation: function _getCatalystOperation(_request) {
  386. return _request && _request.payload && _request.payload.catalystOperation;
  387. },
  388. _getFastPatternDetectionMethods: function _getFastPatternDetectionMethods(op) {
  389. return op === this.CATALYST_OP.DECISIONTREE ? ['tree'] : ['drivers', 'tree'];
  390. },
  391. /**
  392. * If the request errored out, then we want to add the error
  393. * to the view info indicator and not call predict. This means
  394. * we need to return an empty result set to Vida so they don't error out
  395. * and can render the empty FPD base charts.
  396. * @param {Object} _request - original vida request
  397. * @param {Object} errorDesc - description of the current error state
  398. * @returns {Object} empty result and predict status.
  399. */
  400. _handleErrorPossibleKeyDriverRequest: function _handleErrorPossibleKeyDriverRequest(_request, errorDesc) {
  401. var visView = this.visAPI.ownerWidget.getCurrentVis();
  402. // If we have a vis view add an error to the info indicator.
  403. if (visView && visView.infoIndicator) {
  404. var msg = errorDesc.error.caption;
  405. var catId = errorDesc.error && errorDesc.error.categoryId ? errorDesc.error.categoryId : 'predict_error';
  406. visView.infoIndicator.addInfo([{
  407. id: catId,
  408. label: stringResources.get(catId),
  409. items: [{
  410. id: '_' + msg.replace(/\s/g, '_'),
  411. label: msg
  412. }]
  413. }]);
  414. }
  415. // We need to return an empty result so Vida isn't left hanging.
  416. var data = {
  417. result: [],
  418. predictStatus: {}
  419. };
  420. this._requestComplete(_request, data);
  421. },
  422. /**
  423. * The cataclyst service does not behave well when sending queries with empty
  424. * possible drivers. So, we won't send the query (which saves time and resources)
  425. * anyway. Instead we create our own dummy response with an empty result and
  426. * complete the request. Note, we do not need to add a warning here as Vida
  427. * will notify us with a message for the user that the target has no key drivers.
  428. * @param {Object} _request - the original request from Vida.
  429. */
  430. _handleEmptyPossibleKeyDriverRequest: function _handleEmptyPossibleKeyDriverRequest(_request) {
  431. var data = {
  432. result: [],
  433. predictStatus: {}
  434. };
  435. this._requestComplete(_request, data);
  436. },
  437. /**
  438. * If the FPD request fails outright (say with a 500) it will return a response
  439. * with errors we can add to the info indicator. We should log those before
  440. * failing the initial request.
  441. * @param {Object} _request - the initial request
  442. * @param {Object} errorData - holds, among other props, an array of errors
  443. * TODO: We now have a number of ways of dealing with adding information to the
  444. * info indicator depending on what endpoint we are hitting, if there are network
  445. * failures, if the query fails, etc... We need to work to simplify this and add
  446. * consistency with the endpoints and what they return.
  447. */
  448. _fpdRequestFailed: function _fpdRequestFailed(_request, errorData) {
  449. // Deal with adding the failure messages to the info indicator and logging them.
  450. if (this.visView && this.visView.infoIndicator) {
  451. var info = {
  452. label: stringResources.get('predict_errors'),
  453. id: this.PREDICT_ERROR_ID,
  454. items: []
  455. };
  456. if (errorData && errorData.errors && errorData.errors.length) {
  457. errorData.errors.forEach(function (errorDesc) {
  458. info.items.push({
  459. id: errorDesc.code,
  460. label: errorDesc.message
  461. });
  462. });
  463. } else {
  464. // use a generic server message if nothing is supplied
  465. info.items.push({
  466. id: errorData && errorData.code || 500,
  467. label: stringResources.get('catalyst_service_error')
  468. });
  469. }
  470. this.visView.infoIndicator.addInfo([info]);
  471. }
  472. // Notify the caller the request failed.
  473. this._requestFail(_request, new Error(stringResources.get('catalyst_service_internal_server_error')));
  474. },
  475. _handleKeyDriversRequest: function _handleKeyDriversRequest(_request) {
  476. var _this5 = this;
  477. this.visView.infoIndicator.clearMessagesWithIds([this.PREDICT_ERROR_ID, this.PREDICT_WARNING_ID]);
  478. var targetId = this._getTargetId(_request);
  479. var operation = this._getCatalystOperation(_request);
  480. var methods = this._getFastPatternDetectionMethods(operation);
  481. var possibleKeyDriversDesc = this.visAPI.getEnabledPossibleKeyDrivers(targetId);
  482. /*
  483. * There are two possible scenarios when getting the possible key drivers
  484. * 1) There is an error condition.
  485. * b) There are valid possible key drivers
  486. */
  487. // Case 1: error state.
  488. if (possibleKeyDriversDesc.errorState && possibleKeyDriversDesc.errorState.inErrorState) {
  489. this._handleErrorPossibleKeyDriverRequest(_request, possibleKeyDriversDesc.errorState);
  490. } else {
  491. // Case b) which includes empty list of possible key drivers.
  492. var possibleKeyDrivers = possibleKeyDriversDesc.possibleKeyDrivers;
  493. try {
  494. this.visAPI.executeFastPatternDetectionRequest(targetId, possibleKeyDrivers, methods).then(function (data) {
  495. _this5._requestComplete(_request, data);
  496. }, function (e) {
  497. _this5._fpdRequestFailed(_request, e && e.data);
  498. });
  499. } catch (err) {
  500. this.logger.error(err);
  501. }
  502. }
  503. },
  504. _getValidPredictResultTypes: function _getValidPredictResultTypes(methods) {
  505. var validTypes = [];
  506. _.each(methods, function (method) {
  507. switch (method) {
  508. case 'tree':
  509. validTypes.push('TREE');
  510. break;
  511. case 'oneway':
  512. validTypes.push('ONE_WAY');
  513. break;
  514. case 'twoway':
  515. validTypes.push('TWO_WAY');
  516. break;
  517. case 'drivers':
  518. validTypes.push('ONE_WAY');
  519. validTypes.push('TWO_WAY');
  520. break;
  521. default:
  522. }
  523. });
  524. return validTypes;
  525. },
  526. _filterPredictResults: function _filterPredictResults(data, methods) {
  527. var validTypes = this._getValidPredictResultTypes(methods);
  528. if (validTypes && validTypes.length > 0) {
  529. data.results = _.filter(data.results, function (result) {
  530. return validTypes.indexOf(result.type) > -1;
  531. });
  532. }
  533. return data;
  534. },
  535. _getTargetId: function _getTargetId(_request) {
  536. var wa_attributeUniqueName;
  537. var targetId = _request.payload.catalystPayload.target;
  538. // TODO: massage the targetId to the Predict expected format for now
  539. // This will be removed once the bugs have been resolved between VIPR & Predict
  540. var targetMetaDataColumns = this.visAPI.getModule().getMetadataColumns(function (metaDataColumn) {
  541. return metaDataColumn.getId() === targetId;
  542. });
  543. if (targetMetaDataColumns.length) {
  544. wa_attributeUniqueName = ModuleUtils.getColumnPropertyValue(targetMetaDataColumns[0], 'wa_attributeUniqueName');
  545. }
  546. return wa_attributeUniqueName || targetId;
  547. },
  548. _addLocalFiltersToRequest: function _addLocalFiltersToRequest(params) {
  549. var aFilters = this._getWidgetLocalFilters();
  550. if (aFilters) {
  551. params.sessionShapingChange = true;
  552. params.localFilters = this._normalizeFilters(aFilters);
  553. }
  554. },
  555. /**
  556. * Get localFilters of the current widget.
  557. */
  558. _getWidgetLocalFilters: function _getWidgetLocalFilters() {
  559. var localFiltersDefinition = this.visAPI.getLocalFilters();
  560. var queryFilterSpec = new QueryFilterSpec();
  561. queryFilterSpec.addFiltersToSpec(localFiltersDefinition);
  562. if (queryFilterSpec.hasFilterSpec()) {
  563. return queryFilterSpec.getFilterSpec();
  564. }
  565. return null;
  566. },
  567. _normalizeFilters: function _normalizeFilters(aFilters) {
  568. var aFiltersStrings = [];
  569. _.each(aFilters, function (filter) {
  570. // Sort the filter values alphabetically to make sure prediction service will have the same
  571. // cache key if the filter content is the same but the order of filter values are different
  572. // or the order of attributes in the filter expression are different.
  573. filter = this._doSort(filter);
  574. aFiltersStrings.push(JSON.stringify(filter));
  575. }.bind(this));
  576. aFiltersStrings.sort();
  577. var aSortedFilters = [];
  578. _.each(aFiltersStrings, function (filterStr) {
  579. aSortedFilters.push(JSON.parse(filterStr));
  580. });
  581. return aSortedFilters;
  582. },
  583. _doSort: function _doSort(sortObj) {
  584. var sortedArray = [];
  585. var sortedObj;
  586. var aKeys = _.keys(sortObj);
  587. if (aKeys && aKeys.length > 1) {
  588. aKeys.sort();
  589. }
  590. for (var i = 0; i < aKeys.length; i++) {
  591. var key = aKeys[i];
  592. if (key === 'values') {
  593. // If the key is values, will sort an array of value Strings in alphabetically order.
  594. sortedObj = sortObj.values.sort();
  595. } else if (_typeof(sortObj[key]) === 'object') {
  596. sortedObj = this._doSort(sortObj[key]);
  597. } else {
  598. sortedObj = sortObj[key];
  599. }
  600. var obj = {};
  601. obj[key] = sortedObj;
  602. sortedArray.push(obj);
  603. }
  604. return this._toSortedObject(sortedArray);
  605. },
  606. _toSortedObject: function _toSortedObject(sortedArray) {
  607. return _.extend.apply(null, sortedArray);
  608. }
  609. });
  610. return VIPRHandler;
  611. });
  612. //# sourceMappingURL=VIPRDataRequestHandler.js.map