You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2018/12/10 07:24:52 UTC
[22/37] ignite git commit: IGNITE-10318 Web console: Update panels to
use newer UI on "Query notebook" screen.
IGNITE-10318 Web console: Update panels to use newer UI on "Query notebook" screen.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/e1b8686a
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/e1b8686a
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/e1b8686a
Branch: refs/heads/ignite-10189
Commit: e1b8686aa9d7887d462e0f6b04f33501a9f31141
Parents: f965bf5
Author: Ilya Borisov <kl...@gmail.com>
Authored: Fri Dec 7 16:26:00 2018 +0700
Committer: Alexey Kuznetsov <ak...@apache.org>
Committed: Fri Dec 7 16:26:00 2018 +0700
----------------------------------------------------------------------
.../input-dialog/input-dialog.tpl.pug | 1 -
.../query-actions-button/component.ts | 28 +
.../query-actions-button/controller.ts | 53 +
.../query-actions-button/template.pug | 28 +
.../components/queries-notebook/controller.js | 1952 -----------------
.../components/queries-notebook/controller.ts | 2007 ++++++++++++++++++
.../components/queries-notebook/index.js | 2 +
.../queries-notebook/template.tpl.pug | 50 +-
8 files changed, 2139 insertions(+), 1982 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/e1b8686a/modules/web-console/frontend/app/components/input-dialog/input-dialog.tpl.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/input-dialog/input-dialog.tpl.pug b/modules/web-console/frontend/app/components/input-dialog/input-dialog.tpl.pug
index d79cb56..23896e7 100644
--- a/modules/web-console/frontend/app/components/input-dialog/input-dialog.tpl.pug
+++ b/modules/web-console/frontend/app/components/input-dialog/input-dialog.tpl.pug
@@ -21,7 +21,6 @@ include /app/helpers/jade/mixins
form.modal-content(name='ctrl.form' novalidate)
.modal-header
h4.modal-title
- i.fa.fa-clone
span {{ ctrl.options.title }}
button.close(type='button' aria-label='Close' ng-click='$hide()')
svg(ignite-icon="cross")
http://git-wip-us.apache.org/repos/asf/ignite/blob/e1b8686a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/component.ts
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/component.ts b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/component.ts
new file mode 100644
index 0000000..099efc2
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/component.ts
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './template.pug';
+import QueryActionsButton from './controller';
+
+export const component: ng.IComponentOptions = {
+ controller: QueryActionsButton,
+ template,
+ bindings: {
+ actions: '<',
+ item: '<'
+ }
+};
http://git-wip-us.apache.org/repos/asf/ignite/blob/e1b8686a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/controller.ts
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/controller.ts b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/controller.ts
new file mode 100644
index 0000000..e71cf46
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/controller.ts
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export type QueryActions < T > = Array<{text: string, click?(item: T): any, available?(item: T): boolean}>;
+
+export default class QueryActionButton<T> {
+ static $inject = ['$element'];
+
+ item: T;
+
+ actions: QueryActions<T>;
+
+ boundActions: QueryActions<undefined> = [];
+
+ constructor(private el: JQLite) {}
+
+ $postLink() {
+ this.el[0].classList.add('btn-ignite-group');
+ }
+
+ $onChanges(changes: {actions: ng.IChangesObject<QueryActionButton<T>['actions']>}) {
+ if ('actions' in changes) {
+ this.boundActions = changes.actions.currentValue.map((a) => {
+ const action = {...a};
+
+ const click = () => a.click(this.item);
+
+ Object.defineProperty(action, 'click', {
+ get: () => {
+ return typeof a.available === 'function'
+ ? a.available(this.item) ? click : void 0
+ : a.available ? click : void 0;
+ }
+ });
+ return action;
+ });
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/e1b8686a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/template.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/template.pug b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/template.pug
new file mode 100644
index 0000000..32359bf
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/components/query-actions-button/template.pug
@@ -0,0 +1,28 @@
+//-
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+button.btn-ignite.btn-ignite--primary(
+ ng-click='$ctrl.boundActions[0].click()'
+ type='button'
+ ng-disabled='!$ctrl.boundActions[0].click'
+)
+ | {{ ::$ctrl.boundActions[0].text }}
+button.btn-ignite.btn-ignite--primary(
+ bs-dropdown='$ctrl.boundActions'
+ data-placement='bottom-right'
+ type='button'
+)
+ span.icon.fa.fa-caret-down
http://git-wip-us.apache.org/repos/asf/ignite/blob/e1b8686a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
deleted file mode 100644
index 4b1b861..0000000
--- a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
+++ /dev/null
@@ -1,1952 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import _ from 'lodash';
-import {nonEmpty, nonNil} from 'app/utils/lodashMixins';
-import id8 from 'app/utils/id8';
-import 'rxjs/add/operator/mergeMap';
-import 'rxjs/add/operator/merge';
-import 'rxjs/add/operator/switchMap';
-import 'rxjs/add/operator/exhaustMap';
-import 'rxjs/add/operator/distinctUntilChanged';
-
-import { fromPromise } from 'rxjs/observable/fromPromise';
-import { timer } from 'rxjs/observable/timer';
-import { defer } from 'rxjs/observable/defer';
-
-import {CSV} from 'app/services/CSV';
-
-import paragraphRateTemplateUrl from 'views/sql/paragraph-rate.tpl.pug';
-import cacheMetadataTemplateUrl from 'views/sql/cache-metadata.tpl.pug';
-import chartSettingsTemplateUrl from 'views/sql/chart-settings.tpl.pug';
-import messageTemplateUrl from 'views/templates/message.tpl.pug';
-
-// Time line X axis descriptor.
-const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
-
-// Row index X axis descriptor.
-const ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
-
-const NON_COLLOCATED_JOINS_SINCE = '1.7.0';
-
-const COLLOCATED_QUERY_SINCE = [['2.3.5', '2.4.0'], ['2.4.6', '2.5.0'], ['2.5.1-p13', '2.6.0'], '2.7.0'];
-
-const ENFORCE_JOIN_SINCE = [['1.7.9', '1.8.0'], ['1.8.4', '1.9.0'], '1.9.1'];
-
-const LAZY_QUERY_SINCE = [['2.1.4-p1', '2.2.0'], '2.2.1'];
-
-const DDL_SINCE = [['2.1.6', '2.2.0'], '2.3.0'];
-
-const _fullColName = (col) => {
- const res = [];
-
- if (col.schemaName)
- res.push(col.schemaName);
-
- if (col.typeName)
- res.push(col.typeName);
-
- res.push(col.fieldName);
-
- return res.join('.');
-};
-
-let paragraphId = 0;
-
-class Paragraph {
- constructor($animate, $timeout, JavaTypes, errorParser, paragraph) {
- const self = this;
-
- self.id = 'paragraph-' + paragraphId++;
- self.qryType = paragraph.qryType || 'query';
- self.maxPages = 0;
- self.filter = '';
- self.useAsDefaultSchema = false;
- self.localQueryMode = false;
- self.csvIsPreparing = false;
- self.scanningInProgress = false;
-
- _.assign(this, paragraph);
-
- Object.defineProperty(this, 'gridOptions', {value: {
- enableGridMenu: false,
- enableColumnMenus: false,
- flatEntityAccess: true,
- fastWatch: true,
- categories: [],
- rebuildColumns() {
- if (_.isNil(this.api))
- return;
-
- this.categories.length = 0;
-
- this.columnDefs = _.reduce(self.meta, (cols, col, idx) => {
- cols.push({
- displayName: col.fieldName,
- headerTooltip: _fullColName(col),
- field: idx.toString(),
- minWidth: 50,
- cellClass: 'cell-left',
- visible: self.columnFilter(col)
- });
-
- this.categories.push({
- name: col.fieldName,
- visible: self.columnFilter(col),
- enableHiding: true
- });
-
- return cols;
- }, []);
-
- $timeout(() => this.api.core.notifyDataChange('column'));
- },
- adjustHeight() {
- if (_.isNil(this.api))
- return;
-
- this.data = self.rows;
-
- const height = Math.min(self.rows.length, 15) * 30 + 47;
-
- // Remove header height.
- this.api.grid.element.css('height', height + 'px');
-
- $timeout(() => this.api.core.handleWindowResize());
- },
- onRegisterApi(api) {
- $animate.enabled(api.grid.element, false);
-
- this.api = api;
-
- this.rebuildColumns();
-
- this.adjustHeight();
- }
- }});
-
- Object.defineProperty(this, 'chartHistory', {value: []});
-
- Object.defineProperty(this, 'error', {value: {
- root: {},
- message: ''
- }});
-
- this.setError = (err) => {
- this.error.root = err;
- this.error.message = errorParser.extractMessage(err);
-
- let cause = err;
-
- while (nonNil(cause)) {
- if (nonEmpty(cause.className) &&
- _.includes(['SQLException', 'JdbcSQLException', 'QueryCancelledException'], JavaTypes.shortClassName(cause.className))) {
- this.error.message = errorParser.extractMessage(cause.message || cause.className);
-
- break;
- }
-
- cause = cause.cause;
- }
-
- if (_.isEmpty(this.error.message) && nonEmpty(err.className)) {
- this.error.message = 'Internal cluster error';
-
- if (nonEmpty(err.className))
- this.error.message += ': ' + err.className;
- }
- };
- }
-
- resultType() {
- if (_.isNil(this.queryArgs))
- return null;
-
- if (nonEmpty(this.error.message))
- return 'error';
-
- if (_.isEmpty(this.rows))
- return 'empty';
-
- return this.result === 'table' ? 'table' : 'chart';
- }
-
- nonRefresh() {
- return _.isNil(this.rate) || _.isNil(this.rate.stopTime);
- }
-
- table() {
- return this.result === 'table';
- }
-
- chart() {
- return this.result !== 'table' && this.result !== 'none';
- }
-
- nonEmpty() {
- return this.rows && this.rows.length > 0;
- }
-
- queryExecuted() {
- return nonEmpty(this.meta) || nonEmpty(this.error.message);
- }
-
- scanExplain() {
- return this.queryExecuted() && this.queryArgs.type !== 'QUERY';
- }
-
- timeLineSupported() {
- return this.result !== 'pie';
- }
-
- chartColumnsConfigured() {
- return nonEmpty(this.chartKeyCols) && nonEmpty(this.chartValCols);
- }
-
- chartTimeLineEnabled() {
- return nonEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE);
- }
-
- executionInProgress(showLocal = false) {
- return this.loading && (this.localQueryMode === showLocal);
- }
-
- checkScanInProgress(showLocal = false) {
- return this.scanningInProgress && (this.localQueryMode === showLocal);
- }
-
- cancelRefresh($interval) {
- if (this.rate && this.rate.stopTime) {
- $interval.cancel(this.rate.stopTime);
-
- delete this.rate.stopTime;
- }
- }
-
- reset($interval) {
- this.meta = [];
- this.chartColumns = [];
- this.chartKeyCols = [];
- this.chartValCols = [];
- this.error.root = {};
- this.error.message = '';
- this.rows = [];
- this.duration = 0;
-
- this.cancelRefresh($interval);
- }
-}
-
-// Controller for SQL notebook screen.
-export class NotebookCtrl {
- static $inject = ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$filter', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'AgentManager', 'IgniteChartColors', 'IgniteNotebook', 'IgniteNodes', 'uiGridExporterConstants', 'IgniteVersion', 'IgniteActivitiesData', 'JavaTypes', 'IgniteCopyToClipboard', 'CSV', 'IgniteErrorParser', 'DemoInfo'];
-
- /**
- * @param {CSV} CSV
- */
- constructor($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $filter, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMgr, IgniteChartColors, Notebook, Nodes, uiGridExporterConstants, Version, ActivitiesData, JavaTypes, IgniteCopyToClipboard, CSV, errorParser, DemoInfo) {
- const $ctrl = this;
-
- this.CSV = CSV;
- Object.assign(this, { $root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $filter, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMgr, IgniteChartColors, Notebook, Nodes, uiGridExporterConstants, Version, ActivitiesData, JavaTypes, errorParser, DemoInfo });
-
- // Define template urls.
- $ctrl.paragraphRateTemplateUrl = paragraphRateTemplateUrl;
- $ctrl.cacheMetadataTemplateUrl = cacheMetadataTemplateUrl;
- $ctrl.chartSettingsTemplateUrl = chartSettingsTemplateUrl;
- $ctrl.demoStarted = false;
-
- this.isDemo = $root.IgniteDemoMode;
-
- const _tryStopRefresh = function(paragraph) {
- paragraph.cancelRefresh($interval);
- };
-
- const _stopTopologyRefresh = () => {
- if ($scope.notebook && $scope.notebook.paragraphs)
- $scope.notebook.paragraphs.forEach((paragraph) => _tryStopRefresh(paragraph));
- };
-
- $scope.$on('$stateChangeStart', _stopTopologyRefresh);
-
- $scope.caches = [];
-
- $scope.pageSizesOptions = [
- {value: 50, label: '50'},
- {value: 100, label: '100'},
- {value: 200, label: '200'},
- {value: 400, label: '400'},
- {value: 800, label: '800'},
- {value: 1000, label: '1000'}
- ];
-
- $scope.maxPages = [
- {label: 'Unlimited', value: 0},
- {label: '1', value: 1},
- {label: '5', value: 5},
- {label: '10', value: 10},
- {label: '20', value: 20},
- {label: '50', value: 50},
- {label: '100', value: 100}
- ];
-
- $scope.timeLineSpans = ['1', '5', '10', '15', '30'];
-
- $scope.aggregateFxs = ['FIRST', 'LAST', 'MIN', 'MAX', 'SUM', 'AVG', 'COUNT'];
-
- $scope.modes = LegacyUtils.mkOptions(['PARTITIONED', 'REPLICATED', 'LOCAL']);
-
- $scope.loadingText = $root.IgniteDemoMode ? 'Demo grid is starting. Please wait...' : 'Loading query notebook screen...';
-
- $scope.timeUnit = [
- {value: 1000, label: 'seconds', short: 's'},
- {value: 60000, label: 'minutes', short: 'm'},
- {value: 3600000, label: 'hours', short: 'h'}
- ];
-
- $scope.metadata = [];
-
- $scope.metaFilter = '';
-
- $scope.metaOptions = {
- nodeChildren: 'children',
- dirSelectable: true,
- injectClasses: {
- iExpanded: 'fa fa-minus-square-o',
- iCollapsed: 'fa fa-plus-square-o'
- }
- };
-
- const maskCacheName = $filter('defaultName');
-
- // We need max 1800 items to hold history for 30 mins in case of refresh every second.
- const HISTORY_LENGTH = 1800;
-
- const MAX_VAL_COLS = IgniteChartColors.length;
-
- $anchorScroll.yOffset = 55;
-
- $scope.chartColor = function(index) {
- return {color: 'white', 'background-color': IgniteChartColors[index]};
- };
-
- function _chartNumber(arr, idx, dflt) {
- if (idx >= 0 && arr && arr.length > idx && _.isNumber(arr[idx]))
- return arr[idx];
-
- return dflt;
- }
-
- function _min(rows, idx, dflt) {
- let min = _chartNumber(rows[0], idx, dflt);
-
- _.forEach(rows, (row) => {
- const v = _chartNumber(row, idx, dflt);
-
- if (v < min)
- min = v;
- });
-
- return min;
- }
-
- function _max(rows, idx, dflt) {
- let max = _chartNumber(rows[0], idx, dflt);
-
- _.forEach(rows, (row) => {
- const v = _chartNumber(row, idx, dflt);
-
- if (v > max)
- max = v;
- });
-
- return max;
- }
-
- function _sum(rows, idx) {
- let sum = 0;
-
- _.forEach(rows, (row) => sum += _chartNumber(row, idx, 0));
-
- return sum;
- }
-
- function _aggregate(rows, aggFx, idx, dflt) {
- const len = rows.length;
-
- switch (aggFx) {
- case 'FIRST':
- return _chartNumber(rows[0], idx, dflt);
-
- case 'LAST':
- return _chartNumber(rows[len - 1], idx, dflt);
-
- case 'MIN':
- return _min(rows, idx, dflt);
-
- case 'MAX':
- return _max(rows, idx, dflt);
-
- case 'SUM':
- return _sum(rows, idx);
-
- case 'AVG':
- return len > 0 ? _sum(rows, idx) / len : 0;
-
- case 'COUNT':
- return len;
-
- default:
- }
-
- return 0;
- }
-
- function _chartLabel(arr, idx, dflt) {
- if (arr && arr.length > idx && _.isString(arr[idx]))
- return arr[idx];
-
- return dflt;
- }
-
- function _chartDatum(paragraph) {
- let datum = [];
-
- if (paragraph.chartColumnsConfigured()) {
- paragraph.chartValCols.forEach(function(valCol) {
- let index = 0;
- let values = [];
- const colIdx = valCol.value;
-
- if (paragraph.chartTimeLineEnabled()) {
- const aggFx = valCol.aggFx;
- const colLbl = valCol.label + ' [' + aggFx + ']';
-
- if (paragraph.charts && paragraph.charts.length === 1)
- datum = paragraph.charts[0].data;
-
- const chartData = _.find(datum, {series: valCol.label});
-
- const leftBound = new Date();
- leftBound.setMinutes(leftBound.getMinutes() - parseInt(paragraph.timeLineSpan, 10));
-
- if (chartData) {
- const lastItem = _.last(paragraph.chartHistory);
-
- values = chartData.values;
-
- values.push({
- x: lastItem.tm,
- y: _aggregate(lastItem.rows, aggFx, colIdx, index++)
- });
-
- while (values.length > 0 && values[0].x < leftBound)
- values.shift();
- }
- else {
- _.forEach(paragraph.chartHistory, (history) => {
- if (history.tm >= leftBound) {
- values.push({
- x: history.tm,
- y: _aggregate(history.rows, aggFx, colIdx, index++)
- });
- }
- });
-
- datum.push({series: valCol.label, key: colLbl, values});
- }
- }
- else {
- index = paragraph.total;
-
- values = _.map(paragraph.rows, function(row) {
- const xCol = paragraph.chartKeyCols[0].value;
-
- const v = {
- x: _chartNumber(row, xCol, index),
- xLbl: _chartLabel(row, xCol, null),
- y: _chartNumber(row, colIdx, index)
- };
-
- index++;
-
- return v;
- });
-
- datum.push({series: valCol.label, key: valCol.label, values});
- }
- });
- }
-
- return datum;
- }
-
- function _xX(d) {
- return d.x;
- }
-
- function _yY(d) {
- return d.y;
- }
-
- function _xAxisTimeFormat(d) {
- return d3.time.format('%X')(new Date(d));
- }
-
- const _intClasses = ['java.lang.Byte', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
-
- function _intType(cls) {
- return _.includes(_intClasses, cls);
- }
-
- const _xAxisWithLabelFormat = function(paragraph) {
- return function(d) {
- const values = paragraph.charts[0].data[0].values;
-
- const fmt = _intType(paragraph.chartKeyCols[0].type) ? 'd' : ',.2f';
-
- const dx = values[d];
-
- if (!dx)
- return d3.format(fmt)(d);
-
- const lbl = dx.xLbl;
-
- return lbl ? lbl : d3.format(fmt)(d);
- };
- };
-
- function _xAxisLabel(paragraph) {
- return _.isEmpty(paragraph.chartKeyCols) ? 'X' : paragraph.chartKeyCols[0].label;
- }
-
- const _yAxisFormat = function(d) {
- const fmt = d < 1000 ? ',.2f' : '.3s';
-
- return d3.format(fmt)(d);
- };
-
- function _updateCharts(paragraph) {
- $timeout(() => _.forEach(paragraph.charts, (chart) => chart.api.update()), 100);
- }
-
- function _updateChartsWithData(paragraph, newDatum) {
- $timeout(() => {
- if (!paragraph.chartTimeLineEnabled()) {
- const chartDatum = paragraph.charts[0].data;
-
- chartDatum.length = 0;
-
- _.forEach(newDatum, (series) => chartDatum.push(series));
- }
-
- paragraph.charts[0].api.update();
- });
- }
-
- function _yAxisLabel(paragraph) {
- const cols = paragraph.chartValCols;
-
- const tml = paragraph.chartTimeLineEnabled();
-
- return _.isEmpty(cols) ? 'Y' : _.map(cols, function(col) {
- let lbl = col.label;
-
- if (tml)
- lbl += ' [' + col.aggFx + ']';
-
- return lbl;
- }).join(', ');
- }
-
- function _barChart(paragraph) {
- const datum = _chartDatum(paragraph);
-
- if (_.isEmpty(paragraph.charts)) {
- const stacked = paragraph.chartsOptions && paragraph.chartsOptions.barChart
- ? paragraph.chartsOptions.barChart.stacked
- : true;
-
- const options = {
- chart: {
- type: 'multiBarChart',
- height: 400,
- margin: {left: 70},
- duration: 0,
- x: _xX,
- y: _yY,
- xAxis: {
- axisLabel: _xAxisLabel(paragraph),
- tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
- showMaxMin: false
- },
- yAxis: {
- axisLabel: _yAxisLabel(paragraph),
- tickFormat: _yAxisFormat
- },
- color: IgniteChartColors,
- stacked,
- showControls: true,
- legend: {
- vers: 'furious',
- margin: {right: -15}
- }
- }
- };
-
- paragraph.charts = [{options, data: datum}];
-
- _updateCharts(paragraph);
- }
- else
- _updateChartsWithData(paragraph, datum);
- }
-
- function _pieChartDatum(paragraph) {
- const datum = [];
-
- if (paragraph.chartColumnsConfigured() && !paragraph.chartTimeLineEnabled()) {
- paragraph.chartValCols.forEach(function(valCol) {
- let index = paragraph.total;
-
- const values = _.map(paragraph.rows, (row) => {
- const xCol = paragraph.chartKeyCols[0].value;
-
- const v = {
- x: xCol < 0 ? index : row[xCol],
- y: _chartNumber(row, valCol.value, index)
- };
-
- // Workaround for known problem with zero values on Pie chart.
- if (v.y === 0)
- v.y = 0.0001;
-
- index++;
-
- return v;
- });
-
- datum.push({series: paragraph.chartKeyCols[0].label, key: valCol.label, values});
- });
- }
-
- return datum;
- }
-
- function _pieChart(paragraph) {
- let datum = _pieChartDatum(paragraph);
-
- if (datum.length === 0)
- datum = [{values: []}];
-
- paragraph.charts = _.map(datum, function(data) {
- return {
- options: {
- chart: {
- type: 'pieChart',
- height: 400,
- duration: 0,
- x: _xX,
- y: _yY,
- showLabels: true,
- labelThreshold: 0.05,
- labelType: 'percent',
- donut: true,
- donutRatio: 0.35,
- legend: {
- vers: 'furious',
- margin: {right: -15}
- }
- },
- title: {
- enable: true,
- text: data.key
- }
- },
- data: data.values
- };
- });
-
- _updateCharts(paragraph);
- }
-
- function _lineChart(paragraph) {
- const datum = _chartDatum(paragraph);
-
- if (_.isEmpty(paragraph.charts)) {
- const options = {
- chart: {
- type: 'lineChart',
- height: 400,
- margin: { left: 70 },
- duration: 0,
- x: _xX,
- y: _yY,
- xAxis: {
- axisLabel: _xAxisLabel(paragraph),
- tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
- showMaxMin: false
- },
- yAxis: {
- axisLabel: _yAxisLabel(paragraph),
- tickFormat: _yAxisFormat
- },
- color: IgniteChartColors,
- useInteractiveGuideline: true,
- legend: {
- vers: 'furious',
- margin: {right: -15}
- }
- }
- };
-
- paragraph.charts = [{options, data: datum}];
-
- _updateCharts(paragraph);
- }
- else
- _updateChartsWithData(paragraph, datum);
- }
-
- function _areaChart(paragraph) {
- const datum = _chartDatum(paragraph);
-
- if (_.isEmpty(paragraph.charts)) {
- const style = paragraph.chartsOptions && paragraph.chartsOptions.areaChart
- ? paragraph.chartsOptions.areaChart.style
- : 'stack';
-
- const options = {
- chart: {
- type: 'stackedAreaChart',
- height: 400,
- margin: {left: 70},
- duration: 0,
- x: _xX,
- y: _yY,
- xAxis: {
- axisLabel: _xAxisLabel(paragraph),
- tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
- showMaxMin: false
- },
- yAxis: {
- axisLabel: _yAxisLabel(paragraph),
- tickFormat: _yAxisFormat
- },
- color: IgniteChartColors,
- style,
- legend: {
- vers: 'furious',
- margin: {right: -15}
- }
- }
- };
-
- paragraph.charts = [{options, data: datum}];
-
- _updateCharts(paragraph);
- }
- else
- _updateChartsWithData(paragraph, datum);
- }
-
- function _chartApplySettings(paragraph, resetCharts) {
- if (resetCharts)
- paragraph.charts = [];
-
- if (paragraph.chart() && paragraph.nonEmpty()) {
- switch (paragraph.result) {
- case 'bar':
- _barChart(paragraph);
- break;
-
- case 'pie':
- _pieChart(paragraph);
- break;
-
- case 'line':
- _lineChart(paragraph);
- break;
-
- case 'area':
- _areaChart(paragraph);
- break;
-
- default:
- }
- }
- }
-
- $scope.chartRemoveKeyColumn = function(paragraph, index) {
- paragraph.chartKeyCols.splice(index, 1);
-
- _chartApplySettings(paragraph, true);
- };
-
- $scope.chartRemoveValColumn = function(paragraph, index) {
- paragraph.chartValCols.splice(index, 1);
-
- _chartApplySettings(paragraph, true);
- };
-
- $scope.chartAcceptKeyColumn = function(paragraph, item) {
- const accepted = _.findIndex(paragraph.chartKeyCols, item) < 0;
-
- if (accepted) {
- paragraph.chartKeyCols = [item];
-
- _chartApplySettings(paragraph, true);
- }
-
- return false;
- };
-
- const _numberClasses = ['java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Double',
- 'java.lang.Float', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
-
- const _numberType = function(cls) {
- return _.includes(_numberClasses, cls);
- };
-
- $scope.chartAcceptValColumn = function(paragraph, item) {
- const valCols = paragraph.chartValCols;
-
- const accepted = _.findIndex(valCols, item) < 0 && item.value >= 0 && _numberType(item.type);
-
- if (accepted) {
- if (valCols.length === MAX_VAL_COLS - 1)
- valCols.shift();
-
- valCols.push(item);
-
- _chartApplySettings(paragraph, true);
- }
-
- return false;
- };
-
- $scope.scrollParagraphs = [];
-
- $scope.rebuildScrollParagraphs = function() {
- $scope.scrollParagraphs = $scope.notebook.paragraphs.map(function(paragraph) {
- return {
- text: paragraph.name,
- click: 'scrollToParagraph("' + paragraph.id + '")'
- };
- });
- };
-
- $scope.scrollToParagraph = (id) => {
- const idx = _.findIndex($scope.notebook.paragraphs, {id});
-
- if (idx >= 0) {
- if (!_.includes($scope.notebook.expandedParagraphs, idx))
- $scope.notebook.expandedParagraphs = $scope.notebook.expandedParagraphs.concat([idx]);
-
- if ($scope.notebook.paragraphs[idx].ace)
- setTimeout(() => $scope.notebook.paragraphs[idx].ace.focus());
- }
-
- $location.hash(id);
-
- $anchorScroll();
- };
-
- const _hideColumn = (col) => col.fieldName !== '_KEY' && col.fieldName !== '_VAL';
-
- const _allColumn = () => true;
-
- $scope.aceInit = function(paragraph) {
- return function(editor) {
- editor.setAutoScrollEditorIntoView(true);
- editor.$blockScrolling = Infinity;
-
- const renderer = editor.renderer;
-
- renderer.setHighlightGutterLine(false);
- renderer.setShowPrintMargin(false);
- renderer.setOption('fontFamily', 'monospace');
- renderer.setOption('fontSize', '14px');
- renderer.setOption('minLines', '5');
- renderer.setOption('maxLines', '15');
-
- editor.setTheme('ace/theme/chrome');
-
- Object.defineProperty(paragraph, 'ace', { value: editor });
- };
- };
-
- /**
- * Update caches list.
- */
- const _refreshCaches = () => {
- return agentMgr.publicCacheNames()
- .then((cacheNames) => {
- $scope.caches = _.sortBy(_.map(cacheNames, (name) => ({
- label: maskCacheName(name, true),
- value: name
- })), (cache) => cache.label.toLowerCase());
-
- _.forEach($scope.notebook.paragraphs, (paragraph) => {
- if (!_.includes(cacheNames, paragraph.cacheName))
- paragraph.cacheName = _.head(cacheNames);
- });
-
- // Await for demo caches.
- if (!$ctrl.demoStarted && $root.IgniteDemoMode && nonEmpty(cacheNames)) {
- $ctrl.demoStarted = true;
-
- Loading.finish('sqlLoading');
-
- _.forEach($scope.notebook.paragraphs, (paragraph) => $scope.execute(paragraph));
- }
-
- $scope.$applyAsync();
- })
- .catch((err) => Messages.showError(err));
- };
-
- const _startWatch = () => {
- const awaitClusters$ = fromPromise(
- agentMgr.startClusterWatch('Leave Queries', 'default-state'));
-
- const finishLoading$ = defer(() => {
- if (!$root.IgniteDemoMode)
- Loading.finish('sqlLoading');
- }).take(1);
-
- const refreshCaches = (period) => {
- return timer(0, period).exhaustMap(() => _refreshCaches()).merge(finishLoading$);
- };
-
- this.refresh$ = awaitClusters$
- .mergeMap(() => agentMgr.currentCluster$)
- .do(() => Loading.start('sqlLoading'))
- .do(() => {
- _.forEach($scope.notebook.paragraphs, (paragraph) => {
- paragraph.reset($interval);
- });
- })
- .switchMap(() => refreshCaches(5000))
- .subscribe();
- };
-
- const _newParagraph = (paragraph) => {
- return new Paragraph($animate, $timeout, JavaTypes, errorParser, paragraph);
- };
-
- Notebook.find($state.params.noteId)
- .then((notebook) => {
- $scope.notebook = _.cloneDeep(notebook);
-
- $scope.notebook_name = $scope.notebook.name;
-
- if (!$scope.notebook.expandedParagraphs)
- $scope.notebook.expandedParagraphs = [];
-
- if (!$scope.notebook.paragraphs)
- $scope.notebook.paragraphs = [];
-
- $scope.notebook.paragraphs = _.map($scope.notebook.paragraphs, (p) => _newParagraph(p));
-
- if (_.isEmpty($scope.notebook.paragraphs))
- $scope.addQuery();
- else
- $scope.rebuildScrollParagraphs();
- })
- .then(() => {
- if ($root.IgniteDemoMode && sessionStorage.showDemoInfo !== 'true') {
- sessionStorage.showDemoInfo = 'true';
-
- this.DemoInfo.show().then(_startWatch);
- } else
- _startWatch();
- })
- .catch(() => {
- $scope.notebookLoadFailed = true;
-
- Loading.finish('sqlLoading');
- });
-
- $scope.renameNotebook = (name) => {
- if (!name)
- return;
-
- if ($scope.notebook.name !== name) {
- const prevName = $scope.notebook.name;
-
- $scope.notebook.name = name;
-
- Notebook.save($scope.notebook)
- .then(() => $scope.notebook.edit = false)
- .catch((err) => {
- $scope.notebook.name = prevName;
-
- Messages.showError(err);
- });
- }
- else
- $scope.notebook.edit = false;
- };
-
- $scope.removeNotebook = (notebook) => Notebook.remove(notebook);
-
- $scope.renameParagraph = function(paragraph, newName) {
- if (!newName)
- return;
-
- if (paragraph.name !== newName) {
- paragraph.name = newName;
-
- $scope.rebuildScrollParagraphs();
-
- Notebook.save($scope.notebook)
- .then(() => paragraph.edit = false)
- .catch(Messages.showError);
- }
- else
- paragraph.edit = false;
- };
-
- $scope.addParagraph = (paragraph, sz) => {
- if ($scope.caches && $scope.caches.length > 0)
- paragraph.cacheName = _.head($scope.caches).value;
-
- $scope.notebook.paragraphs.push(paragraph);
-
- $scope.notebook.expandedParagraphs.push(sz);
-
- $scope.rebuildScrollParagraphs();
-
- $location.hash(paragraph.id);
- };
-
- $scope.addQuery = function() {
- const sz = $scope.notebook.paragraphs.length;
-
- ActivitiesData.post({ group: 'sql', action: '/queries/add/query' });
-
- const paragraph = _newParagraph({
- name: 'Query' + (sz === 0 ? '' : sz),
- query: '',
- pageSize: $scope.pageSizesOptions[1].value,
- timeLineSpan: $scope.timeLineSpans[0],
- result: 'none',
- rate: {
- value: 1,
- unit: 60000,
- installed: false
- },
- qryType: 'query',
- lazy: true
- });
-
- $scope.addParagraph(paragraph, sz);
-
- $timeout(() => {
- $anchorScroll();
-
- paragraph.ace.focus();
- });
- };
-
- $scope.addScan = function() {
- const sz = $scope.notebook.paragraphs.length;
-
- ActivitiesData.post({ group: 'sql', action: '/queries/add/scan' });
-
- const paragraph = _newParagraph({
- name: 'Scan' + (sz === 0 ? '' : sz),
- query: '',
- pageSize: $scope.pageSizesOptions[1].value,
- timeLineSpan: $scope.timeLineSpans[0],
- result: 'none',
- rate: {
- value: 1,
- unit: 60000,
- installed: false
- },
- qryType: 'scan'
- });
-
- $scope.addParagraph(paragraph, sz);
- };
-
- function _saveChartSettings(paragraph) {
- if (!_.isEmpty(paragraph.charts)) {
- const chart = paragraph.charts[0].api.getScope().chart;
-
- if (!LegacyUtils.isDefined(paragraph.chartsOptions))
- paragraph.chartsOptions = {barChart: {stacked: true}, areaChart: {style: 'stack'}};
-
- switch (paragraph.result) {
- case 'bar':
- paragraph.chartsOptions.barChart.stacked = chart.stacked();
-
- break;
-
- case 'area':
- paragraph.chartsOptions.areaChart.style = chart.style();
-
- break;
-
- default:
- }
- }
- }
-
- $scope.setResult = function(paragraph, new_result) {
- if (paragraph.result === new_result)
- return;
-
- _saveChartSettings(paragraph);
-
- paragraph.result = new_result;
-
- if (paragraph.chart())
- _chartApplySettings(paragraph, true);
- };
-
- $scope.resultEq = function(paragraph, result) {
- return (paragraph.result === result);
- };
-
- $scope.removeParagraph = function(paragraph) {
- Confirm.confirm('Are you sure you want to remove query: "' + paragraph.name + '"?')
- .then(function() {
- $scope.stopRefresh(paragraph);
-
- const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
- return paragraph === item;
- });
-
- const panel_idx = _.findIndex($scope.expandedParagraphs, function(item) {
- return paragraph_idx === item;
- });
-
- if (panel_idx >= 0)
- $scope.expandedParagraphs.splice(panel_idx, 1);
-
- $scope.notebook.paragraphs.splice(paragraph_idx, 1);
-
- $scope.rebuildScrollParagraphs();
-
- Notebook.save($scope.notebook)
- .catch(Messages.showError);
- });
- };
-
- $scope.paragraphExpanded = function(paragraph) {
- const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
- return paragraph === item;
- });
-
- const panel_idx = _.findIndex($scope.notebook.expandedParagraphs, function(item) {
- return paragraph_idx === item;
- });
-
- return panel_idx >= 0;
- };
-
- const _columnFilter = function(paragraph) {
- return paragraph.disabledSystemColumns || paragraph.systemColumns ? _allColumn : _hideColumn;
- };
-
- const _notObjectType = function(cls) {
- return LegacyUtils.isJavaBuiltInClass(cls);
- };
-
- function _retainColumns(allCols, curCols, acceptableType, xAxis, unwantedCols) {
- const retainedCols = [];
-
- const availableCols = xAxis ? allCols : _.filter(allCols, function(col) {
- return col.value >= 0;
- });
-
- if (availableCols.length > 0) {
- curCols.forEach(function(curCol) {
- const col = _.find(availableCols, {label: curCol.label});
-
- if (col && acceptableType(col.type)) {
- col.aggFx = curCol.aggFx;
-
- retainedCols.push(col);
- }
- });
-
- // If nothing was restored, add first acceptable column.
- if (_.isEmpty(retainedCols)) {
- let col;
-
- if (unwantedCols)
- col = _.find(availableCols, (avCol) => !_.find(unwantedCols, {label: avCol.label}) && acceptableType(avCol.type));
-
- if (!col)
- col = _.find(availableCols, (avCol) => acceptableType(avCol.type));
-
- if (col)
- retainedCols.push(col);
- }
- }
-
- return retainedCols;
- }
-
- const _rebuildColumns = function(paragraph) {
- _.forEach(_.groupBy(paragraph.meta, 'fieldName'), function(colsByName, fieldName) {
- const colsByTypes = _.groupBy(colsByName, 'typeName');
-
- const needType = _.keys(colsByTypes).length > 1;
-
- _.forEach(colsByTypes, function(colsByType, typeName) {
- _.forEach(colsByType, function(col, ix) {
- col.fieldName = (needType && !LegacyUtils.isEmptyString(typeName) ? typeName + '.' : '') + fieldName + (ix > 0 ? ix : '');
- });
- });
- });
-
- paragraph.gridOptions.rebuildColumns();
-
- paragraph.chartColumns = _.reduce(paragraph.meta, (acc, col, idx) => {
- if (_notObjectType(col.fieldTypeName)) {
- acc.push({
- label: col.fieldName,
- type: col.fieldTypeName,
- aggFx: $scope.aggregateFxs[0],
- value: idx.toString()
- });
- }
-
- return acc;
- }, []);
-
- if (paragraph.chartColumns.length > 0) {
- paragraph.chartColumns.push(TIME_LINE);
- paragraph.chartColumns.push(ROW_IDX);
- }
-
- // We could accept onl not object columns for X axis.
- paragraph.chartKeyCols = _retainColumns(paragraph.chartColumns, paragraph.chartKeyCols, _notObjectType, true);
-
- // We could accept only numeric columns for Y axis.
- paragraph.chartValCols = _retainColumns(paragraph.chartColumns, paragraph.chartValCols, _numberType, false, paragraph.chartKeyCols);
- };
-
- $scope.toggleSystemColumns = function(paragraph) {
- if (paragraph.disabledSystemColumns)
- return;
-
- paragraph.columnFilter = _columnFilter(paragraph);
-
- paragraph.chartColumns = [];
-
- _rebuildColumns(paragraph);
- };
-
- const _showLoading = (paragraph, enable) => paragraph.loading = enable;
-
- /**
- * @param {Object} paragraph Query
- * @param {Boolean} clearChart Flag is need clear chart model.
- * @param {{columns: Array, rows: Array, responseNodeId: String, queryId: int, hasMore: Boolean}} res Query results.
- * @private
- */
- const _processQueryResult = (paragraph, clearChart, res) => {
- const prevKeyCols = paragraph.chartKeyCols;
- const prevValCols = paragraph.chartValCols;
-
- if (!_.eq(paragraph.meta, res.columns)) {
- paragraph.meta = [];
-
- paragraph.chartColumns = [];
-
- if (!LegacyUtils.isDefined(paragraph.chartKeyCols))
- paragraph.chartKeyCols = [];
-
- if (!LegacyUtils.isDefined(paragraph.chartValCols))
- paragraph.chartValCols = [];
-
- if (res.columns.length) {
- const _key = _.find(res.columns, {fieldName: '_KEY'});
- const _val = _.find(res.columns, {fieldName: '_VAL'});
-
- paragraph.disabledSystemColumns = !(_key && _val) ||
- (res.columns.length === 2 && _key && _val) ||
- (res.columns.length === 1 && (_key || _val));
- }
-
- paragraph.columnFilter = _columnFilter(paragraph);
-
- paragraph.meta = res.columns;
-
- _rebuildColumns(paragraph);
- }
-
- paragraph.page = 1;
-
- paragraph.total = 0;
-
- paragraph.duration = res.duration;
-
- paragraph.queryId = res.hasMore ? res.queryId : null;
-
- paragraph.resNodeId = res.responseNodeId;
-
- paragraph.setError({message: ''});
-
- // Prepare explain results for display in table.
- if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.rows) {
- paragraph.rows = [];
-
- res.rows.forEach((row, i) => {
- const line = res.rows.length - 1 === i ? row[0] : row[0] + '\n';
-
- line.replace(/\"/g, '').split('\n').forEach((ln) => paragraph.rows.push([ln]));
- });
- }
- else
- paragraph.rows = res.rows;
-
- paragraph.gridOptions.adjustHeight(paragraph.rows.length);
-
- const chartHistory = paragraph.chartHistory;
-
- // Clear history on query change.
- if (clearChart) {
- chartHistory.length = 0;
-
- _.forEach(paragraph.charts, (chart) => chart.data.length = 0);
- }
-
- // Add results to history.
- chartHistory.push({tm: new Date(), rows: paragraph.rows});
-
- // Keep history size no more than max length.
- while (chartHistory.length > HISTORY_LENGTH)
- chartHistory.shift();
-
- _showLoading(paragraph, false);
-
- if (_.isNil(paragraph.result) || paragraph.result === 'none' || paragraph.scanExplain())
- paragraph.result = 'table';
- else if (paragraph.chart()) {
- let resetCharts = clearChart;
-
- if (!resetCharts) {
- const curKeyCols = paragraph.chartKeyCols;
- const curValCols = paragraph.chartValCols;
-
- resetCharts = !prevKeyCols || !prevValCols ||
- prevKeyCols.length !== curKeyCols.length ||
- prevValCols.length !== curValCols.length;
- }
-
- _chartApplySettings(paragraph, resetCharts);
- }
- };
-
- const _closeOldQuery = (paragraph) => {
- const nid = paragraph.resNodeId;
-
- if (paragraph.queryId && _.find($scope.caches, ({nodes}) => _.find(nodes, {nid: nid.toUpperCase()})))
- return agentMgr.queryClose(nid, paragraph.queryId);
-
- return $q.when();
- };
-
- /**
- * @param {String} name Cache name.
- * @param {Array.<String>} nids Cache name.
- * @return {Promise<Array.<{nid: string, ip: string, version:string, gridName: string, os: string, client: boolean}>>}
- */
- const cacheNodesModel = (name, nids) => {
- return agentMgr.topology(true)
- .then((nodes) =>
- _.reduce(nodes, (acc, node) => {
- if (_.includes(nids, node.nodeId)) {
- acc.push({
- nid: node.nodeId.toUpperCase(),
- ip: _.head(node.attributes['org.apache.ignite.ips'].split(', ')),
- version: node.attributes['org.apache.ignite.build.ver'],
- gridName: node.attributes['org.apache.ignite.ignite.name'],
- os: `${node.attributes['os.name']} ${node.attributes['os.arch']} ${node.attributes['os.version']}`,
- client: node.attributes['org.apache.ignite.cache.client']
- });
- }
-
- return acc;
- }, [])
- );
- };
-
- /**
- * @param {string} name Cache name.
- * @param {boolean} local Local query.
- * @return {Promise<string>} Nid
- */
- const _chooseNode = (name, local) => {
- if (_.isEmpty(name))
- return Promise.resolve(null);
-
- return agentMgr.cacheNodes(name)
- .then((nids) => {
- if (local) {
- return cacheNodesModel(name, nids)
- .then((nodes) => Nodes.selectNode(nodes, name).catch(() => {}))
- .then((selectedNids) => _.head(selectedNids));
- }
-
- return nids[_.random(0, nids.length - 1)];
- });
- };
-
- const _executeRefresh = (paragraph) => {
- const args = paragraph.queryArgs;
-
- agentMgr.awaitCluster()
- .then(() => _closeOldQuery(paragraph))
- .then(() => args.localNid || _chooseNode(args.cacheName, false))
- .then((nid) => agentMgr.querySql(nid, args.cacheName, args.query, args.nonCollocatedJoins,
- args.enforceJoinOrder, false, !!args.localNid, args.pageSize, args.lazy, args.collocated))
- .then((res) => _processQueryResult(paragraph, false, res))
- .catch((err) => paragraph.setError(err));
- };
-
- const _tryStartRefresh = function(paragraph) {
- _tryStopRefresh(paragraph);
-
- if (_.get(paragraph, 'rate.installed') && paragraph.queryExecuted()) {
- $scope.chartAcceptKeyColumn(paragraph, TIME_LINE);
-
- _executeRefresh(paragraph);
-
- const delay = paragraph.rate.value * paragraph.rate.unit;
-
- paragraph.rate.stopTime = $interval(_executeRefresh, delay, 0, false, paragraph);
- }
- };
-
- const addLimit = (query, limitSize) =>
- `SELECT * FROM (
- ${query}
- ) LIMIT ${limitSize}`;
-
- $scope.nonCollocatedJoinsAvailable = () => {
- return Version.since(this.agentMgr.clusterVersion, NON_COLLOCATED_JOINS_SINCE);
- };
-
- $scope.collocatedJoinsAvailable = () => {
- return Version.since(this.agentMgr.clusterVersion, ...COLLOCATED_QUERY_SINCE);
- };
-
- $scope.enforceJoinOrderAvailable = () => {
- return Version.since(this.agentMgr.clusterVersion, ...ENFORCE_JOIN_SINCE);
- };
-
- $scope.lazyQueryAvailable = () => {
- return Version.since(this.agentMgr.clusterVersion, ...LAZY_QUERY_SINCE);
- };
-
- $scope.ddlAvailable = () => {
- return Version.since(this.agentMgr.clusterVersion, ...DDL_SINCE);
- };
-
- $scope.cacheNameForSql = (paragraph) => {
- return $scope.ddlAvailable() && !paragraph.useAsDefaultSchema ? null : paragraph.cacheName;
- };
-
- $scope.execute = (paragraph, local = false) => {
- const nonCollocatedJoins = !!paragraph.nonCollocatedJoins;
- const enforceJoinOrder = !!paragraph.enforceJoinOrder;
- const lazy = !!paragraph.lazy;
- const collocated = !!paragraph.collocated;
-
- $scope.queryAvailable(paragraph) && _chooseNode(paragraph.cacheName, local)
- .then((nid) => {
- // If we are executing only selected part of query then Notebook shouldn't be saved.
- if (!paragraph.partialQuery)
- Notebook.save($scope.notebook).catch(Messages.showError);
-
- paragraph.localQueryMode = local;
- paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
-
- _showLoading(paragraph, true);
-
- return _closeOldQuery(paragraph)
- .then(() => {
- const query = paragraph.partialQuery || paragraph.query;
-
- const args = paragraph.queryArgs = {
- type: 'QUERY',
- cacheName: $scope.cacheNameForSql(paragraph),
- query,
- pageSize: paragraph.pageSize,
- maxPages: paragraph.maxPages,
- nonCollocatedJoins,
- enforceJoinOrder,
- localNid: local ? nid : null,
- lazy,
- collocated
- };
-
- ActivitiesData.post({ group: 'sql', action: '/queries/execute' });
-
- const qry = args.maxPages ? addLimit(args.query, args.pageSize * args.maxPages) : query;
-
- return agentMgr.querySql(nid, args.cacheName, qry, nonCollocatedJoins, enforceJoinOrder, false, local, args.pageSize, lazy, collocated);
- })
- .then((res) => {
- _processQueryResult(paragraph, true, res);
-
- _tryStartRefresh(paragraph);
- })
- .catch((err) => {
- paragraph.setError(err);
-
- _showLoading(paragraph, false);
-
- $scope.stopRefresh(paragraph);
- })
- .then(() => paragraph.ace.focus());
- });
- };
-
- const _cancelRefresh = (paragraph) => {
- if (paragraph.rate && paragraph.rate.stopTime) {
- delete paragraph.queryArgs;
-
- paragraph.rate.installed = false;
-
- $interval.cancel(paragraph.rate.stopTime);
-
- delete paragraph.rate.stopTime;
- }
- };
-
- $scope.explain = (paragraph) => {
- const nonCollocatedJoins = !!paragraph.nonCollocatedJoins;
- const enforceJoinOrder = !!paragraph.enforceJoinOrder;
- const collocated = !!paragraph.collocated;
-
- if (!$scope.queryAvailable(paragraph))
- return;
-
- if (!paragraph.partialQuery)
- Notebook.save($scope.notebook).catch(Messages.showError);
-
- _cancelRefresh(paragraph);
-
- _showLoading(paragraph, true);
-
- _closeOldQuery(paragraph)
- .then(() => _chooseNode(paragraph.cacheName, false))
- .then((nid) => {
- const args = paragraph.queryArgs = {
- type: 'EXPLAIN',
- cacheName: $scope.cacheNameForSql(paragraph),
- query: 'EXPLAIN ' + (paragraph.partialQuery || paragraph.query),
- pageSize: paragraph.pageSize
- };
-
- ActivitiesData.post({ group: 'sql', action: '/queries/explain' });
-
- return agentMgr.querySql(nid, args.cacheName, args.query, nonCollocatedJoins, enforceJoinOrder, false, false, args.pageSize, false, collocated);
- })
- .then((res) => _processQueryResult(paragraph, true, res))
- .catch((err) => {
- paragraph.setError(err);
-
- _showLoading(paragraph, false);
- })
- .then(() => paragraph.ace.focus());
- };
-
- $scope.scan = (paragraph, local = false) => {
- const cacheName = paragraph.cacheName;
- const caseSensitive = !!paragraph.caseSensitive;
- const filter = paragraph.filter;
- const pageSize = paragraph.pageSize;
-
- $scope.scanAvailable(paragraph) && _chooseNode(cacheName, local)
- .then((nid) => {
- paragraph.localQueryMode = local;
- paragraph.scanningInProgress = true;
-
- Notebook.save($scope.notebook)
- .catch(Messages.showError);
-
- _cancelRefresh(paragraph);
-
- _showLoading(paragraph, true);
-
- _closeOldQuery(paragraph)
- .then(() => {
- paragraph.queryArgs = {
- type: 'SCAN',
- cacheName,
- filter,
- regEx: false,
- caseSensitive,
- near: false,
- pageSize,
- localNid: local ? nid : null
- };
-
- ActivitiesData.post({ group: 'sql', action: '/queries/scan' });
-
- return agentMgr.queryScan(nid, cacheName, filter, false, caseSensitive, false, local, pageSize);
- })
- .then((res) => _processQueryResult(paragraph, true, res))
- .catch((err) => {
- paragraph.setError(err);
-
- _showLoading(paragraph, false);
- })
- .then(() => paragraph.scanningInProgress = false);
- });
- };
-
- function _updatePieChartsWithData(paragraph, newDatum) {
- $timeout(() => {
- _.forEach(paragraph.charts, function(chart) {
- const chartDatum = chart.data;
-
- chartDatum.length = 0;
-
- _.forEach(newDatum, function(series) {
- if (chart.options.title.text === series.key)
- _.forEach(series.values, (v) => chartDatum.push(v));
- });
- });
-
- _.forEach(paragraph.charts, (chart) => chart.api.update());
- });
- }
-
- $scope.nextPage = (paragraph) => {
- _showLoading(paragraph, true);
-
- paragraph.queryArgs.pageSize = paragraph.pageSize;
-
- agentMgr.queryNextPage(paragraph.resNodeId, paragraph.queryId, paragraph.pageSize)
- .then((res) => {
- paragraph.page++;
-
- paragraph.total += paragraph.rows.length;
-
- paragraph.duration = res.duration;
-
- paragraph.rows = res.rows;
-
- if (paragraph.chart()) {
- if (paragraph.result === 'pie')
- _updatePieChartsWithData(paragraph, _pieChartDatum(paragraph));
- else
- _updateChartsWithData(paragraph, _chartDatum(paragraph));
- }
-
- paragraph.gridOptions.adjustHeight(paragraph.rows.length);
-
- _showLoading(paragraph, false);
-
- if (!res.hasMore)
- delete paragraph.queryId;
- })
- .catch((err) => {
- paragraph.setError(err);
-
- _showLoading(paragraph, false);
- })
- .then(() => paragraph.ace && paragraph.ace.focus());
- };
-
- const _export = (fileName, columnDefs, meta, rows, toClipBoard = false) => {
- const csvSeparator = this.CSV.getSeparator();
- let csvContent = '';
-
- const cols = [];
- const excludedCols = [];
-
- _.forEach(meta, (col, idx) => {
- if (columnDefs[idx].visible)
- cols.push(_fullColName(col));
- else
- excludedCols.push(idx);
- });
-
- csvContent += cols.join(csvSeparator) + '\n';
-
- _.forEach(rows, (row) => {
- cols.length = 0;
-
- if (Array.isArray(row)) {
- _.forEach(row, (elem, idx) => {
- if (_.includes(excludedCols, idx))
- return;
-
- cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
- });
- }
- else {
- _.forEach(columnDefs, (col) => {
- if (col.visible) {
- const elem = row[col.fieldName];
-
- cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
- }
- });
- }
-
- csvContent += cols.join(csvSeparator) + '\n';
- });
-
- if (toClipBoard)
- IgniteCopyToClipboard.copy(csvContent);
- else
- LegacyUtils.download('text/csv', fileName, csvContent);
- };
-
- /**
- * Generate file name with query results.
- *
- * @param paragraph {Object} Query paragraph .
- * @param all {Boolean} All result export flag.
- * @returns {string}
- */
- const exportFileName = (paragraph, all) => {
- const args = paragraph.queryArgs;
-
- if (args.type === 'SCAN')
- return `export-scan-${args.cacheName}-${paragraph.name}${all ? '-all' : ''}.csv`;
-
- return `export-query-${paragraph.name}${all ? '-all' : ''}.csv`;
- };
-
- $scope.exportCsvToClipBoard = (paragraph) => {
- _export(exportFileName(paragraph, false), paragraph.gridOptions.columnDefs, paragraph.meta, paragraph.rows, true);
- };
-
- $scope.exportCsv = function(paragraph) {
- _export(exportFileName(paragraph, false), paragraph.gridOptions.columnDefs, paragraph.meta, paragraph.rows);
-
- // paragraph.gridOptions.api.exporter.csvExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
- };
-
- $scope.exportPdf = function(paragraph) {
- paragraph.gridOptions.api.exporter.pdfExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
- };
-
- $scope.exportCsvAll = (paragraph) => {
- paragraph.csvIsPreparing = true;
-
- const args = paragraph.queryArgs;
-
- return Promise.resolve(args.localNid || _chooseNode(args.cacheName, false))
- .then((nid) => args.type === 'SCAN'
- ? agentMgr.queryScanGetAll(nid, args.cacheName, args.query, !!args.regEx, !!args.caseSensitive, !!args.near, !!args.localNid)
- : agentMgr.querySqlGetAll(nid, args.cacheName, args.query, !!args.nonCollocatedJoins, !!args.enforceJoinOrder, false, !!args.localNid, !!args.lazy, !!args.collocated))
- .then((res) => _export(exportFileName(paragraph, true), paragraph.gridOptions.columnDefs, res.columns, res.rows))
- .catch(Messages.showError)
- .then(() => {
- paragraph.csvIsPreparing = false;
-
- return paragraph.ace && paragraph.ace.focus();
- });
- };
-
- // $scope.exportPdfAll = function(paragraph) {
- // $http.post('/api/v1/agent/query/getAll', {query: paragraph.query, cacheName: paragraph.cacheName})
- // .then(({data}) {
- // _export(paragraph.name + '-all.csv', data.meta, data.rows);
- // })
- // .catch(Messages.showError);
- // };
-
- $scope.rateAsString = function(paragraph) {
- if (paragraph.rate && paragraph.rate.installed) {
- const idx = _.findIndex($scope.timeUnit, function(unit) {
- return unit.value === paragraph.rate.unit;
- });
-
- if (idx >= 0)
- return ' ' + paragraph.rate.value + $scope.timeUnit[idx].short;
-
- paragraph.rate.installed = false;
- }
-
- return '';
- };
-
- $scope.startRefresh = function(paragraph, value, unit) {
- paragraph.rate.value = value;
- paragraph.rate.unit = unit;
- paragraph.rate.installed = true;
-
- if (paragraph.queryExecuted() && !paragraph.scanExplain())
- _tryStartRefresh(paragraph);
- };
-
- $scope.stopRefresh = function(paragraph) {
- paragraph.rate.installed = false;
-
- _tryStopRefresh(paragraph);
- };
-
- $scope.paragraphTimeSpanVisible = function(paragraph) {
- return paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled();
- };
-
- $scope.paragraphTimeLineSpan = function(paragraph) {
- if (paragraph && paragraph.timeLineSpan)
- return paragraph.timeLineSpan.toString();
-
- return '1';
- };
-
- $scope.applyChartSettings = function(paragraph) {
- _chartApplySettings(paragraph, true);
- };
-
- $scope.queryAvailable = function(paragraph) {
- return paragraph.query && !paragraph.loading;
- };
-
- $scope.queryTooltip = function(paragraph, action) {
- if ($scope.queryAvailable(paragraph))
- return;
-
- if (paragraph.loading)
- return 'Waiting for server response';
-
- return 'Input text to ' + action;
- };
-
- $scope.scanAvailable = function(paragraph) {
- return $scope.caches.length && !(paragraph.loading || paragraph.csvIsPreparing);
- };
-
- $scope.scanTooltip = function(paragraph) {
- if ($scope.scanAvailable(paragraph))
- return;
-
- if (paragraph.loading)
- return 'Waiting for server response';
-
- return 'Select cache to export scan results';
- };
-
- $scope.clickableMetadata = function(node) {
- return node.type.slice(0, 5) !== 'index';
- };
-
- $scope.dblclickMetadata = function(paragraph, node) {
- paragraph.ace.insert(node.name);
-
- setTimeout(() => paragraph.ace.focus(), 1);
- };
-
- $scope.importMetadata = function() {
- Loading.start('loadingCacheMetadata');
-
- $scope.metadata = [];
-
- agentMgr.metadata()
- .then((metadata) => {
- $scope.metadata = _.sortBy(_.filter(metadata, (meta) => {
- const cache = _.find($scope.caches, { value: meta.cacheName });
-
- if (cache) {
- meta.name = (cache.sqlSchema || '"' + meta.cacheName + '"') + '.' + meta.typeName;
- meta.displayName = (cache.sqlSchema || meta.maskedName) + '.' + meta.typeName;
-
- if (cache.sqlSchema)
- meta.children.unshift({type: 'plain', name: 'cacheName: ' + meta.maskedName, maskedName: meta.maskedName});
-
- meta.children.unshift({type: 'plain', name: 'mode: ' + cache.mode, maskedName: meta.maskedName});
- }
-
- return cache;
- }), 'name');
- })
- .catch(Messages.showError)
- .then(() => Loading.finish('loadingCacheMetadata'));
- };
-
- $scope.showResultQuery = function(paragraph) {
- if (!_.isNil(paragraph)) {
- const scope = $scope.$new();
-
- if (paragraph.queryArgs.type === 'SCAN') {
- scope.title = 'SCAN query';
-
- const filter = paragraph.queryArgs.filter;
-
- if (_.isEmpty(filter))
- scope.content = [`SCAN query for cache: <b>${maskCacheName(paragraph.queryArgs.cacheName, true)}</b>`];
- else
- scope.content = [`SCAN query for cache: <b>${maskCacheName(paragraph.queryArgs.cacheName, true)}</b> with filter: <b>${filter}</b>`];
- }
- else if (paragraph.queryArgs.query .startsWith('EXPLAIN ')) {
- scope.title = 'Explain query';
- scope.content = paragraph.queryArgs.query.split(/\r?\n/);
- }
- else {
- scope.title = 'SQL query';
- scope.content = paragraph.queryArgs.query.split(/\r?\n/);
- }
-
- // Attach duration and selected node info
- scope.meta = `Duration: ${$filter('duration')(paragraph.duration)}.`;
- scope.meta += paragraph.localQueryMode ? ` Node ID8: ${id8(paragraph.resNodeId)}` : '';
-
- // Show a basic modal from a controller
- $modal({scope, templateUrl: messageTemplateUrl, show: true});
- }
- };
-
- $scope.showStackTrace = function(paragraph) {
- if (!_.isNil(paragraph)) {
- const scope = $scope.$new();
-
- scope.title = 'Error details';
- scope.content = [];
-
- const tab = ' ';
-
- const addToTrace = (item) => {
- if (nonNil(item)) {
- scope.content.push((scope.content.length > 0 ? tab : '') + errorParser.extractFullMessage(item));
-
- addToTrace(item.cause);
-
- _.forEach(item.suppressed, (sup) => addToTrace(sup));
- }
- };
-
- addToTrace(paragraph.error.root);
-
- // Show a basic modal from a controller
- $modal({scope, templateUrl: messageTemplateUrl, show: true});
- }
- };
- }
-
- $onDestroy() {
- if (this.refresh$)
- this.refresh$.unsubscribe();
- }
-}