http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/app/components/page-queries/template.tpl.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-queries/template.tpl.pug b/modules/web-console/frontend/app/components/page-queries/template.tpl.pug new file mode 100644 index 0000000..b2173f7 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-queries/template.tpl.pug @@ -0,0 +1,385 @@ +//- + 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. + +include /app/helpers/jade/mixins + +mixin btn-toolbar(btn, click, tip, focusId) + i.btn.btn-default.fa(class=btn ng-click=click bs-tooltip='' data-title=tip ignite-on-click-focus=focusId data-trigger='hover' data-placement='bottom') + +mixin btn-toolbar-data(btn, kind, tip) + i.btn.btn-default.fa(class=btn ng-click=`setResult(paragraph, '${kind}')` ng-class=`{active: resultEq(paragraph, '${kind}')}` bs-tooltip='' data-title=tip data-trigger='hover' data-placement='bottom') + +mixin result-toolbar + .btn-group(ng-model='paragraph.result' ng-click='$event.stopPropagation()' style='left: 50%; margin: 0 0 0 -70px;display: block;') + +btn-toolbar-data('fa-table', 'table', 'Show data in tabular form') + +btn-toolbar-data('fa-bar-chart', 'bar', 'Show bar chart<br/>By default first column - X values, second column - Y values<br/>In case of one column it will be treated as Y values') + +btn-toolbar-data('fa-pie-chart', 'pie', 'Show pie chart<br/>By default first column - pie labels, second column - pie values<br/>In case of one column it will be treated as pie values') + +btn-toolbar-data('fa-line-chart', 'line', 'Show line chart<br/>By default first column - X values, second column - Y values<br/>In case of one column it will be treated as Y values') + +btn-toolbar-data('fa-area-chart', 'area', 'Show area chart<br/>By default first column - X values, second column - Y values<br/>In case of one column it will be treated as Y values') + +mixin chart-settings + .total.row + .col-xs-7 + .chart-settings-link(ng-show='paragraph.chart && paragraph.chartColumns.length > 0') + a(title='Click to show chart settings dialog' ng-click='$event.stopPropagation()' bs-popover data-template-url='{{ $ctrl.chartSettingsTemplateUrl }}' data-placement='bottom' data-auto-close='1' data-trigger='click') + i.fa.fa-bars + | Chart settings + div(ng-show='paragraphTimeSpanVisible(paragraph)') + label Show + button.select-manual-caret.btn.btn-default(ng-model='paragraph.timeLineSpan' ng-change='applyChartSettings(paragraph)' bs-options='item for item in timeLineSpans' bs-select data-caret-html='<span class="caret"></span>') + label min + + div + label Duration: #[b {{paragraph.duration | duration}}] + label.margin-left-dflt(ng-show='paragraph.localQueryMode') NodeID8: #[b {{paragraph.resNodeId | id8}}] + .col-xs-2 + +result-toolbar + +mixin notebook-rename + .docs-header.notebook-header + h1.col-sm-6(ng-hide='notebook.edit') + label(style='max-width: calc(100% - 60px)') {{notebook.name}} + .btn-group(ng-if='!demo') + +btn-toolbar('fa-pencil', 'notebook.edit = true;notebook.editName = notebook.name', 'Rename notebook') + +btn-toolbar('fa-trash', 'removeNotebook(notebook)', 'Remove notebook') + h1.col-sm-6(ng-show='notebook.edit') + i.btn.fa.fa-floppy-o(ng-show='notebook.editName' ng-click='renameNotebook(notebook.editName)' bs-tooltip data-title='Save notebook name' data-trigger='hover') + .input-tip + input.form-control(ng-model='notebook.editName' required ignite-on-enter='renameNotebook(notebook.editName)' ignite-on-escape='notebook.edit = false;') + h1.pull-right + a.dropdown-toggle(style='margin-right: 20px' data-toggle='dropdown' bs-dropdown='scrollParagraphs' data-placement='bottom-right') Scroll to query + span.caret + button.btn.btn-default(style='margin-top: 2px' ng-click='addQuery()' ignite-on-click-focus=focusId) + i.fa.fa-fw.fa-plus + | Add query + + button.btn.btn-default(style='margin-top: 2px' ng-click='addScan()' ignite-on-click-focus=focusId) + i.fa.fa-fw.fa-plus + | Add scan + +mixin notebook-error + h2 Failed to load notebook + label.col-sm-12 Notebook not accessible any more. Go back to configuration or open to another notebook. + button.h3.btn.btn-primary(ui-sref='base.configuration.tabs.advanced.clusters') Back to configuration + +mixin paragraph-rename + .col-sm-6(ng-hide='paragraph.edit') + i.fa(ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"') + label {{paragraph.name}} + + .btn-group(ng-hide='notebook.paragraphs.length > 1') + +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name; $event.stopPropagation();', 'Rename query', 'paragraph-name-{{paragraph.id}}') + + .btn-group(ng-show='notebook.paragraphs.length > 1' ng-click='$event.stopPropagation();') + +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name;', 'Rename query', 'paragraph-name-{{paragraph.id}}') + +btn-toolbar('fa-remove', 'removeParagraph(paragraph)', 'Remove query') + + .col-sm-6(ng-show='paragraph.edit') + i.tipLabel.fa(style='float: left;' ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"') + i.tipLabel.fa.fa-floppy-o(style='float: right;' ng-show='paragraph.editName' ng-click='renameParagraph(paragraph, paragraph.editName); $event.stopPropagation();' bs-tooltip data-title='Save query name' data-trigger='hover') + .input-tip + input.form-control(id='paragraph-name-{{paragraph.id}}' ng-model='paragraph.editName' required ng-click='$event.stopPropagation();' ignite-on-enter='renameParagraph(paragraph, paragraph.editName)' ignite-on-escape='paragraph.edit = false') + +mixin query-settings + .panel-top-align + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Configure periodical execution of last successfully executed query') Refresh rate: + button.btn.btn-default.fa.fa-clock-o.tipLabel(ng-class='{"btn-info": paragraph.rate && paragraph.rate.installed}' bs-popover data-template-url='{{ $ctrl.paragraphRateTemplateUrl }}' data-placement='left' data-auto-close='1' data-trigger='click') {{rateAsString(paragraph)}} + + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Max number of rows to show in query result as one page') Page size: + button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.pageSize' bs-select bs-options='item for item in pageSizes') + + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Limit query max results to specified number of pages') Max pages: + button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.maxPages' bs-select bs-options='item.value as item.label for item in maxPages') + + .panel-tip-container + .row(ng-if='nonCollocatedJoinsAvailable(paragraph)') + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Non-collocated joins is a special mode that allow to join data across cluster without collocation.<br/>\ + Nested joins are not supported for now.<br/>\ + <b>NOTE</b>: In some cases it may consume more heap memory or may take a long time than collocated joins.' data-trigger='hover') + input(type='checkbox' ng-model='paragraph.nonCollocatedJoins') + span Allow non-collocated joins + .row(ng-if='enforceJoinOrderAvailable(paragraph)') + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Enforce join order of tables in the query.<br/>\ + If <b>set</b>, then query optimizer will not reorder tables within join.<br/>\ + <b>NOTE:</b> It is not recommended to enable this property unless you have verified that\ + indexes are not selected in optimal order.' data-trigger='hover') + input(type='checkbox' ng-model='paragraph.enforceJoinOrder') + span Enforce join order + .row(ng-if='lazyQueryAvailable(paragraph)') + label.tipLabel(bs-tooltip data-placement='bottom' data-title='By default Ignite attempts to fetch the whole query result set to memory and send it to the client.<br/>\ + For small and medium result sets this provides optimal performance and minimize duration of internal database locks, thus increasing concurrency.<br/>\ + If result set is too big to fit in available memory this could lead to excessive GC pauses and even OutOfMemoryError.<br/>\ + Use this flag as a hint for Ignite to fetch result set lazily, thus minimizing memory consumption at the cost of moderate performance hit.' data-trigger='hover') + input(type='checkbox' ng-model='paragraph.lazy') + span Lazy result set + +mixin query-actions + button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' ng-click='execute(paragraph)') + div + i.fa.fa-fw.fa-play(ng-hide='paragraph.executionInProgress(false)') + i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.executionInProgress(false)') + span.tipLabelExecute Execute + button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' ng-click='execute(paragraph, true)') + div + i.fa.fa-fw.fa-play(ng-hide='paragraph.executionInProgress(true)') + i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.executionInProgress(true)') + span.tipLabelExecute Execute on selected node + + + a.btn.btn-default(ng-disabled='!queryAvailable(paragraph)' ng-click='explain(paragraph)' data-placement='bottom' bs-tooltip='' data-title='{{queryTooltip(paragraph, "explain query")}}') Explain + +mixin table-result-heading-query + .total.row + .col-xs-7 + grid-column-selector(grid-api='paragraph.gridOptions.api') + .fa.fa-bars.icon + label Page: #[b {{paragraph.page}}] + label.margin-left-dflt Results so far: #[b {{paragraph.rows.length + paragraph.total}}] + label.margin-left-dflt Duration: #[b {{paragraph.duration | duration}}] + label.margin-left-dflt(ng-show='paragraph.localQueryMode') NodeID8: #[b {{paragraph.resNodeId | id8}}] + .col-xs-2 + div(ng-if='paragraph.qryType === "query"') + +result-toolbar + .col-xs-3 + .pull-right + .btn-group.panel-tip-container + button.btn.btn-primary.btn--with-icon( + ng-click='exportCsv(paragraph)' + + ng-disabled='paragraph.loading' + + bs-tooltip='' + ng-attr-title='{{ queryTooltip(paragraph, "export query results") }}' + + data-trigger='hover' + data-placement='bottom' + ) + svg(ignite-icon='csv' ng-if='!paragraph.csvIsPreparing') + i.fa.fa-fw.fa-refresh.fa-spin(ng-if='paragraph.csvIsPreparing') + span Export + + -var options = [{ text: 'Export', click: 'exportCsv(paragraph)' }, { text: 'Export all', click: 'exportCsvAll(paragraph)' }, { divider: true }, { text: '<span title="Copy current result page to clipboard">Copy to clipboard</span>', click: 'exportCsvToClipBoard(paragraph)' }] + button.btn.dropdown-toggle.btn-primary( + ng-disabled='paragraph.loading' + + bs-dropdown=`${JSON.stringify(options)}` + + data-toggle='dropdown' + data-container='body' + data-placement='bottom-right' + data-html='true' + ) + span.caret + + + +mixin table-result-heading-scan + .total.row + .col-xs-7 + grid-column-selector(grid-api='paragraph.gridOptions.api') + .fa.fa-bars.icon + label Page: #[b {{paragraph.page}}] + label.margin-left-dflt Results so far: #[b {{paragraph.rows.length + paragraph.total}}] + label.margin-left-dflt Duration: #[b {{paragraph.duration | duration}}] + label.margin-left-dflt(ng-show='paragraph.localQueryMode') NodeID8: #[b {{paragraph.resNodeId | id8}}] + .col-xs-2 + div(ng-if='paragraph.qryType === "query"') + +result-toolbar + .col-xs-3 + .pull-right + .btn-group.panel-tip-container + // TODO: replace this logic for exporting under one component + button.btn.btn-primary.btn--with-icon( + ng-click='exportCsv(paragraph)' + + ng-disabled='paragraph.loading || paragraph.csvIsPreparing' + + bs-tooltip='' + ng-attr-title='{{ scanTooltip(paragraph) }}' + + data-trigger='hover' + data-placement='bottom' + ) + svg(ignite-icon='csv' ng-if='!paragraph.csvIsPreparing') + i.fa.fa-fw.fa-refresh.fa-spin(ng-if='paragraph.csvIsPreparing') + span Export + + -var options = [{ text: "Export", click: 'exportCsv(paragraph)' }, { text: 'Export all', click: 'exportCsvAll(paragraph)' }, { divider: true }, { text: '<span title="Copy current result page to clipboard">Copy to clipboard</span>', click: 'exportCsvToClipBoard(paragraph)' }] + button.btn.dropdown-toggle.btn-primary( + ng-disabled='paragraph.loading || paragraph.csvIsPreparing' + + bs-dropdown=`${JSON.stringify(options)}` + + data-toggle='dropdown' + data-container='body' + data-placement='bottom-right' + data-html='true' + ) + span.caret + +mixin table-result-body + .grid(ui-grid='paragraph.gridOptions' ui-grid-resize-columns ui-grid-exporter) + +mixin chart-result + div(ng-hide='paragraph.scanExplain()') + +chart-settings + .empty(ng-show='paragraph.chartColumns.length > 0 && !paragraph.chartColumnsConfigured()') Cannot display chart. Please configure axis using #[b Chart settings] + .empty(ng-show='paragraph.chartColumns.length == 0') Cannot display chart. Result set must contain Java build-in type columns. Please change query and execute it again. + div(ng-show='paragraph.chartColumnsConfigured()') + div(ng-show='paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()') + div(ng-repeat='chart in paragraph.charts') + nvd3(options='chart.options' data='chart.data' api='chart.api') + .empty(ng-show='!paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' column for X-axis. Please use another column for X-axis or switch to another chart. + .empty(ng-show='paragraph.scanExplain()') + .row + .col-xs-4.col-xs-offset-4 + +result-toolbar + label.margin-top-dflt Charts do not support #[b Explain] and #[b Scan] query + +mixin paragraph-scan + .panel-heading(bs-collapse-toggle) + .row + +paragraph-rename + .panel-collapse(role='tabpanel' bs-collapse-target) + .col-sm-12.sql-controls + .col-sm-3 + +dropdown-required('Cache:', 'paragraph.cacheName', '"cache"', 'true', 'false', 'Choose cache', 'caches') + .col-sm-3 + +text-enabled('Filter:', 'paragraph.filter', '"filter"', true, false, 'Enter filter') + label.btn.btn-default.ignite-form-field__btn(ng-click='paragraph.caseSensitive = !paragraph.caseSensitive') + input(type='checkbox' ng-model='paragraph.caseSensitive') + span(bs-tooltip data-title='Select this checkbox for case sensitive search') Cs + label.tipLabel(bs-tooltip data-placement='bottom' data-title='Max number of rows to show in query result as one page') Page size: + button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.pageSize' bs-select bs-options='item for item in pageSizes') + + .col-sm-12.sql-controls + button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' ng-click='scan(paragraph)') + div + i.fa.fa-fw.fa-play(ng-hide='paragraph.checkScanInProgress(false)') + i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.checkScanInProgress(false)') + span.tipLabelExecute Scan + + button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' ng-click='scan(paragraph, true)') + i.fa.fa-fw.fa-play(ng-hide='paragraph.checkScanInProgress(true)') + i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.checkScanInProgress(true)') + span.tipLabelExecute Scan on selected node + + .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' ng-switch='paragraph.resultType()') + .error(ng-switch-when='error') Error: {{paragraph.error.message}} + .empty(ng-switch-when='empty') Result set is empty. Duration: #[b {{paragraph.duration | duration}}] + .table(ng-switch-when='table') + +table-result-heading-scan + +table-result-body + .footer.clearfix() + .pull-left + | Showing results for scan of #[b {{ paragraph.queryArgs.cacheName | defaultName }}] + span(ng-if='paragraph.queryArgs.filter') with filter: #[b {{ paragraph.queryArgs.filter }}] + span(ng-if='paragraph.queryArgs.localNid') on node: #[b {{ paragraph.queryArgs.localNid | limitTo:8 }}] + + -var nextVisibleCondition = 'paragraph.resultType() != "error" && paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || paragraph.chart() && !paragraph.scanExplain())' + + .pull-right(ng-show=`${nextVisibleCondition}` ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') + i.fa.fa-chevron-circle-right + a Next + +mixin paragraph-query + .row.panel-heading(bs-collapse-toggle) + +paragraph-rename + .panel-collapse(role='tabpanel' bs-collapse-target) + .col-sm-12 + .col-xs-8.col-sm-9(style='border-right: 1px solid #eee') + .sql-editor(ignite-ace='{onLoad: aceInit(paragraph), theme: "chrome", mode: "sql", require: ["ace/ext/language_tools"],' + + 'advanced: {enableSnippets: false, enableBasicAutocompletion: true, enableLiveAutocompletion: true}}' + ng-model='paragraph.query') + .col-xs-4.col-sm-3 + div(ng-show='caches.length > 0' style='padding: 5px 10px' st-table='displayedCaches' st-safe-src='caches') + lable.labelField.labelFormField Caches: + i.fa.fa-database.tipField(title='Click to show cache types metadata dialog' bs-popover data-template-url='{{ $ctrl.cacheMetadataTemplateUrl }}' data-placement='bottom' data-trigger='click' data-container='#{{ paragraph.id }}') + .input-tip + input.form-control(type='text' st-search='label' placeholder='Filter caches...') + table.links + tbody.scrollable-y(style='max-height: 15em; display: block;') + tr(ng-repeat='cache in displayedCaches track by cache.name') + td(style='width: 100%') + input.labelField(id='cache_{{ [paragraph.id, $index].join("_") }}' type='radio' value='{{cache.name}}' ng-model='paragraph.cacheName') + label(for='cache_{{ [paragraph.id, $index].join("_") }} ' ng-bind-html='cache.label') + .settings-row + .row(ng-if='ddlAvailable(paragraph)') + label.tipLabel.use-cache(bs-tooltip data-placement='bottom' + data-title= + 'Use selected cache as default schema name.<br/>\ + This will allow to execute query on specified cache without specify schema name.<br/>\ + <b>NOTE:</b> In future version of Ignite this feature will be removed.' + data-trigger='hover') + input(type='checkbox' ng-model='paragraph.useAsDefaultSchema') + span Use selected cache as default schema name + .empty-caches(ng-show='displayedCaches.length == 0 && caches.length != 0') + label Wrong caches filter + .empty-caches(ng-show='caches.length == 0') + label No caches + .col-sm-12.sql-controls + +query-actions + + .pull-right + +query-settings + .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' ng-switch='paragraph.resultType()') + .error(ng-switch-when='error') + label Error: {{paragraph.error.message}} + br + a(ng-show='paragraph.resultType() === "error"' ng-click='showStackTrace(paragraph)') Show more + .empty(ng-switch-when='empty') Result set is empty. Duration: #[b {{paragraph.duration | duration}}] + .table(ng-switch-when='table') + +table-result-heading-query + +table-result-body + .chart(ng-switch-when='chart') + +chart-result + .footer.clearfix(ng-show='paragraph.resultType() !== "error"') + a.pull-left(ng-click='showResultQuery(paragraph)') Show query + + -var nextVisibleCondition = 'paragraph.resultType() !== "error" && paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || paragraph.chart() && !paragraph.scanExplain())' + + .pull-right(ng-show=`${nextVisibleCondition}` ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') + i.fa.fa-chevron-circle-right + a Next + +.row + .docs-content + header + h1 Queries + cluster-selector + + .row(ng-if='notebook' bs-affix style='margin-bottom: 20px;') + +notebook-rename + + ignite-information(data-title='With query notebook you can' style='margin-bottom: 30px') + ul + li Create any number of queries + li Execute and explain SQL queries + li Execute scan queries + li View data in tabular form and as charts + + div(ng-if='notebookLoadFailed' style='text-align: center') + +notebook-error + + div(ng-if='notebook' ignite-loading='sqlLoading' ignite-loading-text='{{ loadingText }}' ignite-loading-position='top') + .docs-body.paragraphs + .panel-group(bs-collapse ng-model='notebook.expandedParagraphs' data-allow-multiple='true' data-start-collapsed='false') + + .panel-paragraph(ng-repeat='paragraph in notebook.paragraphs' id='{{paragraph.id}}' ng-form='form_{{paragraph.id}}') + .panel.panel-default(ng-if='paragraph.qryType === "scan"') + +paragraph-scan + .panel.panel-default(ng-if='paragraph.qryType === "query"') + +paragraph-query
http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/app/modules/agent/AgentManager.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js index 752b4f0..7668132 100644 --- a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js +++ b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js @@ -38,7 +38,18 @@ class ConnectionState { this.state = State.DISCONNECTED; } + updateCluster(cluster) { + this.cluster = cluster; + this.cluster.connected = !!_.find(this.clusters, {id: this.cluster.id}); + + return cluster; + } + update(demo, count, clusters) { + _.forEach(clusters, (cluster) => { + cluster.name = cluster.id; + }); + this.clusters = clusters; if (_.isNil(this.cluster)) @@ -142,6 +153,7 @@ export default class IgniteAgentManager { }; self.socket.on('connect_error', onDisconnect); + self.socket.on('disconnect', onDisconnect); self.socket.on('agents:stat', ({clusters, count}) => { @@ -152,6 +164,8 @@ export default class IgniteAgentManager { self.connectionSbj.next(conn); }); + self.socket.on('cluster:changed', (cluster) => this.updateCluster(cluster)); + self.socket.on('user:notifications', (notification) => this.UserNotifications.notification = notification); } @@ -163,6 +177,31 @@ export default class IgniteAgentManager { } } + updateCluster(newCluster) { + const state = this.connectionSbj.getValue(); + + const oldCluster = _.find(state.clusters, (cluster) => cluster.id === newCluster.id); + + if (!_.isNil(oldCluster)) { + oldCluster.nids = newCluster.nids; + oldCluster.addresses = newCluster.addresses; + oldCluster.clusterVersion = newCluster.clusterVersion; + oldCluster.active = newCluster.active; + + this.connectionSbj.next(state); + } + } + + switchCluster(cluster) { + const state = this.connectionSbj.getValue(); + + state.updateCluster(cluster); + + this.connectionSbj.next(state); + + this.saveToStorage(cluster); + } + /** * @param states * @returns {Promise} @@ -212,6 +251,8 @@ export default class IgniteAgentManager { self.connectionSbj.next(conn); + this.modalSubscription && this.modalSubscription.unsubscribe(); + self.modalSubscription = this.connectionSbj.subscribe({ next: ({state}) => { switch (state) { @@ -252,6 +293,8 @@ export default class IgniteAgentManager { self.connectionSbj.next(conn); + this.modalSubscription && this.modalSubscription.unsubscribe(); + self.modalSubscription = this.connectionSbj.subscribe({ next: ({state}) => { switch (state) { @@ -633,7 +676,6 @@ export default class IgniteAgentManager { } /** - /** * @param {String} nid Node id. * @param {String} cacheName Cache name. * @param {String} filter Filter text. @@ -664,4 +706,17 @@ export default class IgniteAgentManager { return this.queryScan(nid, cacheName, filter, regEx, caseSensitive, near, local, pageSz) .then(fetchResult); } + + /** + * Change cluster active state. + * + * @returns {Promise} + */ + toggleClusterState() { + const state = this.connectionSbj.getValue(); + const active = !state.cluster.active; + + return this.visorTask('toggleClusterState', null, active) + .then(() => state.updateCluster(Object.assign(state.cluster, { active }))); + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/app/modules/sql/Notebook.data.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/sql/Notebook.data.js b/modules/web-console/frontend/app/modules/sql/Notebook.data.js deleted file mode 100644 index 3f98bed..0000000 --- a/modules/web-console/frontend/app/modules/sql/Notebook.data.js +++ /dev/null @@ -1,168 +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. - */ - -const DEMO_NOTEBOOK = { - name: 'SQL demo', - paragraphs: [ - { - name: 'Query with refresh rate', - cacheName: 'CarCache', - pageSize: 100, - limit: 0, - query: [ - 'SELECT count(*)', - 'FROM "CarCache".Car' - ].join('\n'), - result: 'bar', - timeLineSpan: '1', - rate: { - value: 3, - unit: 1000, - installed: true - } - }, - { - name: 'Simple query', - cacheName: 'CarCache', - pageSize: 100, - limit: 0, - query: 'SELECT * FROM "CarCache".Car', - result: 'table', - timeLineSpan: '1', - rate: { - value: 30, - unit: 1000, - installed: false - } - }, - { - name: 'Query with aggregates', - cacheName: 'ParkingCache', - pageSize: 100, - limit: 0, - query: [ - 'SELECT p.name, count(*) AS cnt', - 'FROM "ParkingCache".Parking p', - 'INNER JOIN "CarCache".Car c', - ' ON (p.id) = (c.parkingId)', - 'GROUP BY P.NAME' - ].join('\n'), - result: 'table', - timeLineSpan: '1', - rate: { - value: 30, - unit: 1000, - installed: false - } - } - ], - expandedParagraphs: [0, 1, 2] -}; - -export default class NotebookData { - static $inject = ['$rootScope', '$http', '$q']; - - constructor($root, $http, $q) { - this.demo = $root.IgniteDemoMode; - - this.initLatch = null; - this.notebooks = null; - - this.$http = $http; - this.$q = $q; - } - - load() { - if (this.demo) { - if (this.initLatch) - return this.initLatch; - - return this.initLatch = this.$q.when(this.notebooks = [DEMO_NOTEBOOK]); - } - - return this.initLatch = this.$http.get('/api/v1/notebooks') - .then(({data}) => this.notebooks = data) - .catch(({data}) => Promise.reject(data)); - } - - read() { - if (this.initLatch) - return this.initLatch; - - return this.load(); - } - - find(_id) { - return this.read() - .then(() => { - const notebook = this.demo ? this.notebooks[0] : _.find(this.notebooks, {_id}); - - if (_.isNil(notebook)) - return this.$q.reject('Failed to load notebook.'); - - return notebook; - }); - } - - findIndex(notebook) { - return this.read() - .then(() => _.findIndex(this.notebooks, {_id: notebook._id})); - } - - save(notebook) { - if (this.demo) - return this.$q.when(DEMO_NOTEBOOK); - - return this.$http.post('/api/v1/notebooks/save', notebook) - .then(({data}) => { - const idx = _.findIndex(this.notebooks, {_id: data._id}); - - if (idx >= 0) - this.notebooks[idx] = data; - else - this.notebooks.push(data); - - return data; - }) - .catch(({data}) => Promise.reject(data)); - } - - remove(notebook) { - if (this.demo) - return this.$q.reject(`Removing "${notebook.name}" notebook is not supported.`); - - const key = {_id: notebook._id}; - - return this.$http.post('/api/v1/notebooks/remove', key) - .then(() => { - const idx = _.findIndex(this.notebooks, key); - - if (idx >= 0) { - this.notebooks.splice(idx, 1); - - if (idx < this.notebooks.length) - return this.notebooks[idx]; - } - - if (this.notebooks.length > 0) - return this.notebooks[this.notebooks.length - 1]; - - return null; - }) - .catch(({data}) => Promise.reject(data)); - } -} http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/app/modules/sql/Notebook.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/sql/Notebook.service.js b/modules/web-console/frontend/app/modules/sql/Notebook.service.js deleted file mode 100644 index b0bb64f..0000000 --- a/modules/web-console/frontend/app/modules/sql/Notebook.service.js +++ /dev/null @@ -1,74 +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. - */ - -export default class Notebook { - static $inject = ['$state', 'IgniteConfirm', 'IgniteMessages', 'IgniteNotebookData']; - - /** - * @param $state - * @param confirmModal - * @param Messages - * @param {NotebookData} NotebookData - */ - constructor($state, confirmModal, Messages, NotebookData) { - this.$state = $state; - this.confirmModal = confirmModal; - this.Messages = Messages; - this.NotebookData = NotebookData; - } - - read() { - return this.NotebookData.read(); - } - - create(name) { - return this.NotebookData.save({name}); - } - - save(notebook) { - return this.NotebookData.save(notebook); - } - - find(_id) { - return this.NotebookData.find(_id); - } - - _openNotebook(idx) { - return this.NotebookData.read() - .then((notebooks) => { - const nextNotebook = notebooks.length > idx ? notebooks[idx] : _.last(notebooks); - - if (nextNotebook) - this.$state.go('base.sql.notebook', {noteId: nextNotebook._id}); - else - this.$state.go('base.configuration.tabs.advanced.clusters'); - }); - } - - remove(notebook) { - return this.confirmModal.confirm(`Are you sure you want to remove notebook: "${notebook.name}"?`) - .then(() => this.NotebookData.findIndex(notebook)) - .then((idx) => { - this.NotebookData.remove(notebook) - .then(() => { - if (this.$state.includes('base.sql.notebook') && this.$state.params.noteId === notebook._id) - return this._openNotebook(idx); - }) - .catch(this.Messages.showError); - }); - } -} http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/app/modules/sql/notebook.controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/sql/notebook.controller.js b/modules/web-console/frontend/app/modules/sql/notebook.controller.js deleted file mode 100644 index 68d318a..0000000 --- a/modules/web-console/frontend/app/modules/sql/notebook.controller.js +++ /dev/null @@ -1,62 +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 templateUrl from 'views/sql/notebook-new.tpl.pug'; - -// Controller that load notebooks in navigation bar . -export default ['$scope', '$modal', '$state', 'IgniteMessages', 'IgniteNotebook', - (scope, $modal, $state, Messages, Notebook) => { - // Pre-fetch modal dialogs. - const nameModal = $modal({scope, templateUrl, show: false}); - - scope.create = (name) => { - return Notebook.create(name) - .then((notebook) => { - nameModal.hide(); - - $state.go('base.sql.notebook', {noteId: notebook._id}); - }) - .catch(Messages.showError); - }; - - scope.createNotebook = () => nameModal.$promise.then(nameModal.show); - - Notebook.read() - .then((notebooks) => { - scope.$watchCollection(() => notebooks, (changed) => { - if (_.isEmpty(changed)) - return scope.notebooks = []; - - scope.notebooks = [ - {text: 'Create new notebook', click: scope.createNotebook}, - {divider: true} - ]; - - _.forEach(changed, (notebook) => scope.notebooks.push({ - data: notebook, - action: { - icon: 'fa-trash', - click: (item) => Notebook.remove(item) - }, - text: notebook.name, - sref: `base.sql.notebook({noteId:"${notebook._id}"})` - })); - }); - }) - .catch(Messages.showError); - } -];
