http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml b/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml new file mode 100644 index 0000000..5d96e28 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml @@ -0,0 +1,38 @@ +# 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. + +--- +language: node_js +node_js: + - "0.12" + +sudo: false + +cache: + directories: + - node_modules + +before_install: + - "npm config set spin false" + - "npm install -g npm@^2" + +install: + - npm install -g bower + - npm install + - bower install + +script: + - npm test
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js index db65f1f..8a367c9 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js @@ -25,9 +25,13 @@ var app = new EmberApp({ //valid values are `default`, `bootstrap2`, `bootstrap3` or false 'theme': 'bootstrap3' }, + vendorFiles: { + 'handlebars.js': null + }, hinting: false }); +app.import('bower_components/ember/ember-template-compiler.js'); app.import('bower_components/bootstrap/dist/js/bootstrap.js'); app.import('bower_components/bootstrap/dist/css/bootstrap.css'); app.import('bower_components/bootstrap/dist/css/bootstrap.css.map', { @@ -42,5 +46,6 @@ app.import('vendor/codemirror/sql-hint.js'); app.import('vendor/codemirror/show-hint.js'); app.import('vendor/codemirror/codemirror.css'); app.import('vendor/codemirror/show-hint.css'); +app.import('vendor/dagre.min.js'); module.exports = app.toTree(); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js index fd8c1d7..df44c37 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js @@ -19,17 +19,17 @@ import Ember from 'ember'; export default Ember.Component.extend({ - click: function () { - this.toggleProperty('message.isExpanded'); - - if (!this.get('message.isExpanded')) { - this.sendAction('removeLater', this.get('message')); - } - }, - actions: { remove: function () { this.sendAction('removeMessage', this.get('message')); + }, + + toggleMessage: function() { + this.toggleProperty('message.isExpanded'); + + if (!this.get('message.isExpanded')) { + this.sendAction('removeLater', this.get('message')); + } } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js index 4425b59..f2081f2 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js @@ -28,6 +28,11 @@ export default Ember.Component.extend({ if (this.get('isExpanded')) { this.sendAction('expanded', this.get('heading'), this.get('toggledParam')); } + }, + + sendControlAction: function(action) { + this.set('controlAction', action); + this.sendAction('controlAction', this.get('heading'), this.get('toggledParam')); } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js index b9179bb..790e84b 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js @@ -25,10 +25,32 @@ export default Ember.Component.extend(Ember.I18n.TranslateableProperties, { }.bind(this)); }.on('didInsertElement'), + keyPress: function(e) { + Ember.run.debounce(this, function() { + if (e.which === 13) { + this.send('ok'); + } else if (e.which === 27) { + this.send('close'); + } + }, 200) + }, + + setupEvents: function() { + this.$(document).on('keyup', Ember.$.proxy(this.keyPress, this)); + }.on('didInsertElement'), + + destroyEvents: function() { + this.$(document).off('keyup', Ember.$.proxy(this.keyPress, this)); + }.on('willDestroyElement'), + actions: { ok: function () { this.$('.modal').modal('hide'); this.sendAction('ok'); + }, + close: function () { + this.$('.modal').modal('hide'); + this.sendAction('close'); } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js new file mode 100644 index 0000000..5a37f13 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js @@ -0,0 +1,32 @@ +/** +* 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 Ember from 'ember'; + + +export default Ember.Component.extend({ + tagName: 'notifications', + classNames: ['notifications-container'], + notifications : Ember.computed.alias('notify.notifications'), + + actions: { + removeNotification: function(notification) { + this.notify.removeNotification(notification); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js index ad98beb..4ab1dba 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js @@ -24,27 +24,25 @@ export default Ember.Component.extend({ var slider; - this.$(function() { - if (!self.get('numberRange.from') && !self.get('numberRange.to')) { - self.get('numberRange').set('from', self.get('numberRange.min')); - self.get('numberRange').set('to', self.get('numberRange.max')); - } + if (!self.get('numberRange.from') && !self.get('numberRange.to')) { + self.get('numberRange').set('from', self.get('numberRange.min')); + self.get('numberRange').set('to', self.get('numberRange.max')); + } - slider = self.$( ".slider" ).slider({ - range: true, - min: self.get('numberRange.min'), - max: self.get('numberRange.max'), - units: self.get('numberRange.units'), - values: [ self.get('numberRange.from'), self.get('numberRange.to') ], - slide: function (event, ui) { - self.set('numberRange.from', ui.values[0]); - self.set('numberRange.to', ui.values[1]); - }, + slider = self.$( ".slider" ).slider({ + range: true, + min: self.get('numberRange.min'), + max: self.get('numberRange.max'), + units: self.get('numberRange.units'), + values: [ self.get('numberRange.from'), self.get('numberRange.to') ], + slide: function (event, ui) { + self.set('numberRange.from', ui.values[0]); + self.set('numberRange.to', ui.values[1]); + }, - change: function () { - self.sendAction('rangeChanged', self.get('numberRange')); - } - }); + change: function () { + self.sendAction('rangeChanged', self.get('numberRange')); + } }); this.set('slider', slider); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js new file mode 100644 index 0000000..0ef6768 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js @@ -0,0 +1,121 @@ +/** + * 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 Ember from 'ember'; + +export default Ember.Component.extend({ + tabClassNames : "fa queries-icon query-context-tab", + openOverlayAction : 'openOverlay', + closeOverlayAction : 'closeOverlay', + + tabs: [ + Ember.Object.create({ + iconClass: 'fa-code', + action: 'setDefaultActive' + }), + Ember.Object.create({ + iconClass: 'fa-gear', + action: 'toggleOverlay', + template: 'settings', + outlet: 'overlay', + into: 'open-queries' + }), + Ember.Object.create({ + iconClass: 'fa-bar-chart', + action: 'toggleOverlay', + template: 'visual-explain', + outlet: 'overlay', + into: 'index' + }), + Ember.Object.create({ + iconClass: 'text-icon', + text: 'TEZ', + action: 'toggleOverlay', + template: 'tez-ui', + outlet: 'overlay', + into: 'index' + }), + Ember.Object.create({ + iconClass: 'fa-envelope', + action: 'toggleOverlay', + template: 'messages', + outlet: 'overlay', + into: 'open-queries', + badgeProperty: 'count' + }) + ], + + setDefaultTab: function() { + var defaultTab = this.get('tabs.firstObject'); + + defaultTab.set('active', true); + this.set('default', defaultTab); + this.set('active', defaultTab); + }.on('init'), + + setupTabsBadges: function() { + var tabs = this.get('tabs'); + var self = this; + + tabs.map(function(tab) { + if (tab.get('badgeProperty')) { + var controller = self.container.lookup('controller:' + tab.get('template')); + tab.set('controller', controller); + + Ember.oneWay(tab, 'badge', 'controller.count'); + } + }); + }.on('init'), + + closeActiveOverlay: function() { + this.sendAction('closeOverlayAction', this.get('active')); + }, + + openOverlay: function(tab) { + this.closeActiveOverlay(); + this.set('active.active', false); + tab.set('active', true); + this.set('active', tab); + this.sendAction('openOverlayAction', tab); + }, + + setDefaultActive: function() { + var active = this.get('active'); + var defaultTab = this.get('default'); + + if (active !== defaultTab) { + this.closeActiveOverlay(); + defaultTab.set('active', true); + active.set('active', false); + this.set('active', defaultTab); + } + }, + + actions: { + toggleOverlay: function(tab) { + if (tab !== this.get('default') && tab.get('active')) { + this.setDefaultActive(); + } else { + this.openOverlay(tab); + } + }, + + setDefaultActive: function() { + this.setDefaultActive(); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js index 34c1f4b..51f09be 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js @@ -23,15 +23,60 @@ export default Typeahead.extend(Ember.I18n.TranslateableProperties, { didInsertElement: function() { this._super(); - if(!this.get('selection') && this.get('content.firstObject')) { + if (!this.get('selection') && this.get('content.firstObject')) { this.set('selection', this.get('content.firstObject')); } this.selectize.on('dropdown_close', Ember.$.proxy(this.onClose, this)); }, + removeExcludedObserver: function() { + var self = this; + var options = this.get('content'); + + if (!options) { + options = this.removeExcluded(true); + this.set('content', options); + } else { + this.removeExcluded(); + } + }.observes('[email protected]').on('init'), + + removeExcluded: function(shouldReturn) { + var self = this; + var excluded = this.get('excluded') || []; + var options = this.get('options'); + var selection = this.get('selection'); + var objectToModify = this.get('content'); + var objectsToRemove = []; + var objectsToAdd = []; + + if (!options) { + return; + } + + if (shouldReturn) { + objectToModify = Ember.copy(options); + } + + if (options) { + options.forEach(function(option, index) { + if (excluded.contains(option) && option !== selection) { + objectsToRemove.push(option); + } else if (!objectToModify.contains(option)) { + objectsToAdd.push(option); + } + }); + } + + objectToModify.removeObjects(objectsToRemove); + objectToModify.pushObjects(objectsToAdd); + + return objectToModify; + }, + onClose: function() { - if(!this.get('selection') && this.get('prevSelection')) { + if (!this.get('selection') && this.get('prevSelection')) { this.set('selection', this.get('prevSelection')); } }, http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js deleted file mode 100644 index be70566..0000000 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js +++ /dev/null @@ -1,47 +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 Ember from 'ember'; - -export default Ember.ArrayController.extend({ - pushObject: function (object) { - object.typeClass = 'alert-' + object.type; - this._super(object); - this.removeLater(object); - }, - - removeLater: function (object) { - var self = this; - - Ember.run.later(function() { - if (!object.isExpanded) { - self.removeObject(object); - } - }, 5000); - }, - - actions: { - remove: function (message) { - this.removeObject(message); - }, - - removeLater: function (message) { - this.removeLater(message); - } - } -}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js index 6a259ba..0b103cd 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js @@ -30,13 +30,13 @@ export default Ember.ArrayController.extend({ dbTables: Ember.computed.alias('controllers.' + constants.namingConventions.tables), dbColumns: Ember.computed.alias('controllers.' + constants.namingConventions.columns), - _handleTablesError: function (err) { - this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.tables"); + _handleTablesError: function (error) { + this.notify.error(error.responseJSON.message, error.responseJSON.trace); this.set('isLoading', false); }, - _handleColumnsError: function (err) { - this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.columns"); + _handleColumnsError: function (error) { + this.notify.error(error.responseJSON.message, error.responseJSON.trace); this.set('isLoading', false); }, @@ -191,7 +191,52 @@ export default Ember.ArrayController.extend({ return defer.promise; }, + tableControls: Ember.A([ + Ember.Object.create({ + icon: 'fa-list', + action: 'loadSampleData', + tooltip: Ember.I18n.t('tooltips.loadSample') + }) + ]), + + panelIconActions: function () { + return [ + Ember.Object.create({ + icon: 'fa-refresh', + action: 'refreshDatabaseExplorer' + }) + ]; + }.property(), + actions: { + refreshDatabaseExplorer: function() { + var self = this; + var selectedDatabase = this.get('selectedDatabase'); + + this.store.unloadAll('database'); + this.store.fetchAll('database').then(function() { + var database = self.get('model').findBy('id', selectedDatabase.get('id')); + self.set('selectedDatabase', database); + }).catch(function(response) { + self.notify.error(response.responseJSON.message, response.responseJSON.trace); + }); + }, + + loadSampleData: function(tableName, database) { + var self = this; + this.send('addQuery', Ember.I18n.t('titles.tableSample', { tableName: tableName })); + + Ember.run.later(function() { + var query = constants.sampleDataQuery.fmt(tableName); + + self.set('selectedDatabase', database); + self.get('openQueries.currentQuery') + .set('fileContent', query); + + self.send('executeQuery'); + }); + }, + getTables: function (dbName) { var database = this.findBy('name', dbName), tables = database.tables, @@ -257,6 +302,7 @@ export default Ember.ArrayController.extend({ resultsTab = this.get('tabs').findBy('view', constants.namingConventions.databaseSearch), tableSearchResults = this.get('tableSearchResults'); + this.set('tablesSearchTerm', searchTerm); resultsTab.set('visible', true); this.set('selectedTab', resultsTab); this.set('columnSearchTerm', ''); @@ -351,4 +397,4 @@ export default Ember.ArrayController.extend({ }); } } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js index 954b42b..df2d088 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js @@ -42,10 +42,19 @@ export default Ember.Controller.extend({ visualExplain: Ember.computed.alias('controllers.' + constants.namingConventions.visualExplain), tezUI: Ember.computed.alias('controllers.' + constants.namingConventions.tezUI), + isQueryTabActive: function() { + return !this.get('tezUI.showOverlay') && !this.get('visualExplain.showOverlay') && !this.get('settings.showOverlay'); + }.property('tezUI.showOverlay', 'visualExplain.showOverlay', 'settings.showOverlay'), + shouldShowTez: function() { return this.get('model.dagId') && this.get('tezUI.isTezViewAvailable'); }.property('model.dagId', 'tezUI.isTezViewAvailable'), + shouldShowVisualExplain: function () { + return this.get('openQueries.currentQuery.fileContent'); + }.property('openQueries.currentQuery.fileContent'), + + canExecute: function () { var isModelRunning = this.get('model.isRunning'); var hasParams = this.get('queryParams.length'); @@ -84,11 +93,12 @@ export default Ember.Controller.extend({ currentParams.setObjects(updatedParams); }.observes('openQueries.currentQuery.fileContent'), - _executeQuery: function (shouldExplain) { + _executeQuery: function (shouldExplain, shouldGetVisualExplain) { var queryId, query, finalQuery, job, + defer = Ember.RSVP.defer(), originalModel = this.get('model'); job = this.store.createRecord(constants.namingConventions.job, { @@ -110,15 +120,55 @@ export default Ember.Controller.extend({ query = this.get('openQueries').getQueryForModel(originalModel); - finalQuery = this.buildQuery(query, shouldExplain); + query = this.buildQuery(query, shouldExplain, shouldGetVisualExplain); + + // for now we won't support multiple queries + // buildQuery will return false it multiple queries + // are selected + if (!query) { + originalModel.set('isRunning', false); + defer.reject({ + responseJSON: { + message: 'Running multiple queries is not supported.' + } + }); + + return defer.promise; + } + + finalQuery = query; finalQuery = this.bindQueryParams(finalQuery); finalQuery = this.prependQuerySettings(finalQuery); job.set('forcedContent', finalQuery); + if (shouldGetVisualExplain) { + return this.getVisualExplainJson(job, originalModel); + } + return this.saveQuery(job, originalModel); }, + getVisualExplainJson: function (job, originalModel) { + var self = this; + var defer = Ember.RSVP.defer(); + + job.save().then(function () { + self.get('results').getResultsJson(job).then(function (json) { + defer.resolve(json); + originalModel.set('isRunning', undefined); + }, function (err) { + defer.reject(err); + originalModel.set('isRunning', undefined); + }); + }, function (err) { + defer.reject(err); + originalModel.set('isRunning', undefined); + }); + + return defer.promise; + }, + saveQuery: function (job, originalModel) { var defer = Ember.RSVP.defer(), self = this, @@ -158,35 +208,41 @@ export default Ember.Controller.extend({ return query; }, - buildQuery: function (query, shouldExplain) { + buildQuery: function (query, shouldExplain, shouldGetVisualExplain) { var selections = this.get('openQueries.highlightedText'), isQuerySelected = selections && selections[0] !== "", - queryComponents = this.extractComponents(query.get('fileContent')), + queryContent = query ? query.get('fileContent') : '', + queryComponents = this.extractComponents(queryContent), finalQuery = '', - queries; + queries = null; if (isQuerySelected) { - queries = selections.map(function (s) { - return s.replace(";", ""); - }); - } else { - queries = queryComponents.queryString.split(';'); - queries = queries.filter(Boolean); + queryComponents.queryString = selections.join(''); + } + + queries = queryComponents.queryString.split(';'); + queries = queries.map(function(s) { + return s.trim(); + }); + queries = queries.filter(Boolean); + + // return false if multiple queries are selected + // @FIXME: Remove this to support multiple queries + if (queries.length > 1) { + return false; } queries = queries.map(function (query) { if (shouldExplain) { - if (query.indexOf(constants.namingConventions.explainPrefix) === -1) { + query = query.replace(/explain|formatted/gi, '').trim(); + + if (shouldGetVisualExplain) { + return constants.namingConventions.explainFormattedPrefix + query; + } else { return constants.namingConventions.explainPrefix + query; } - - return query; } else { - if (query.indexOf(constants.namingConventions.explainPrefix) > -1) { - return query.replace(constants.namingConventions.explainPrefix, ''); - } - - return query; + return query.replace(/explain|formatted/gi, '').trim(); } }); @@ -199,6 +255,7 @@ export default Ember.Controller.extend({ } finalQuery += queries.join(";"); + finalQuery += ";"; return finalQuery; }, @@ -278,6 +335,10 @@ export default Ember.Controller.extend({ }); }.observes('content'), + selectedDatabaseChanged: function() { + this.set('content.dataBase', this.get('databases.selectedDatabase.name')); + }.observes('databases.selectedDatabase'), + csvUrl: function () { if (this.get('content.constructor.typeKey') !== constants.namingConventions.job) { return; @@ -309,7 +370,7 @@ export default Ember.Controller.extend({ items.push( Ember.Object.create({ title: Ember.I18n.t('buttons.saveCsv'), - href: this.get('csvUrl') + action: 'downloadAsCSV' }) ); } @@ -350,7 +411,7 @@ export default Ember.Controller.extend({ }).then(function (response) { self.pollSaveToHDFS(response); }, function (response) { - self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results"); + self.notify.error(response.responseJSON.message, response.responseJSON.trace); }); }, @@ -367,7 +428,7 @@ export default Ember.Controller.extend({ self.set('content.isRunning', false); } }, function (response) { - self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results"); + self.notify.error(response.responseJSON.message, response.responseJSON.trace); }); }, 2000); }, @@ -386,6 +447,25 @@ export default Ember.Controller.extend({ this.saveToHDFS(); }, + downloadAsCSV: function() { + var self = this, + defer = Ember.RSVP.defer(); + + this.send('openModal', 'modal-save', { + heading: "modals.download.csv", + text: this.get('content.title'), + defer: defer + }); + + defer.promise.then(function (text) { + // download file ... + var urlString = "%@/?fileName=%@.csv"; + var url = self.get('csvUrl'); + url = urlString.fmt(url, text); + window.open(url); + }); + }, + insertUdf: function (item) { var query = this.get('openQueries').getQueryForModel(this.get('model')); @@ -418,17 +498,16 @@ export default Ember.Controller.extend({ addQuery: (function () { var idCounter = 0; - return function () { + return function (workSheetName) { var model = this.store.createRecord(constants.namingConventions.savedQuery, { dataBase: this.get('databases.selectedDatabase.name'), - title: 'New Query', - type: constants.namingConventions.savedQuery, + title: workSheetName ? workSheetName : Ember.I18n.t('titles.query.tab'), queryFile: '', id: 'fixture_' + idCounter }); - if (idCounter) { - model.set('title', model.get('title') + ' (' + idCounter + ')') + if (idCounter && !workSheetName) { + model.set('title', model.get('title') + ' (' + idCounter + ')'); } idCounter++; @@ -438,26 +517,34 @@ export default Ember.Controller.extend({ }()), saveQuery: function () { + //case 1. Save a new query from a new query tab -> route changes to new id + //case 2. Save a new query from an existing query tab -> route changes to new id + //case 3. Save a new query from a job tab -> route doesn't change + //case 4. Update an existing query tab. -> route doesn't change + var self = this, - wasNew = this.get('model.isNew'), defer = Ember.RSVP.defer(); this.set('model.dataBase', this.get('databases.selectedDatabase.name')); - this.send('openModal', 'modal-save', { - heading: "modals.save.heading", + this.send('openModal', 'modal-save-query', { + heading: 'modals.save.heading', + message: 'modals.save.overwrite', text: this.get('content.title'), + content: this.get('content'), defer: defer }); - defer.promise.then(function (text) { - self.get('content').set('title', text); - - self.get('openQueries').save(self.get('content')).then(function () { - if (wasNew) { - self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, self.get('model.id')); - } - }); + defer.promise.then(function (result) { + if (result.get('overwrite')) { + self.get('openQueries').save(self.get('content'), null, true, result.get('text')); + } else { + self.get('openQueries').save(self.get('content'), null, false, result.get('text')).then(function (newId) { + if (self.get('model.constructor.typeKey') !== constants.namingConventions.job) { + self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, newId); + } + }); + } }); }, @@ -476,7 +563,8 @@ export default Ember.Controller.extend({ self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id')); }, function (err) { - self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query"); + var errorBody = err.responseJSON.trace ? err.responseJSON.trace : false; + self.notify.error(err.responseJSON.message, errorBody); }); }, @@ -488,11 +576,13 @@ export default Ember.Controller.extend({ self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id')); }, function (err) { - self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query"); + this.notify.error(err.responseJSON.message, err.responseJSON.trace); }); }, toggleOverlay: function (targetController) { + var self = this; + if (this.get('visualExplain.showOverlay') && targetController !== 'visualExplain') { this.set('visualExplain.showOverlay', false); } else if (this.get('tezUI.showOverlay') && targetController !== 'tezUI') { @@ -501,12 +591,28 @@ export default Ember.Controller.extend({ this.set('settings.showOverlay', false); } + if (!targetController) { + return; + } + if (targetController !== 'settings') { //set content for visual explain and tez ui. this.set(targetController + '.content', this.get('content')); } - this.toggleProperty(targetController + '.showOverlay'); + if (targetController === 'visualExplain' && !this.get(targetController + '.showOverlay')) { + this._executeQuery(true, true).then(function (json) { + //this condition should be changed once we change the way of retrieving this json + if (json['STAGE PLANS']['Stage-1']) { + self.set(targetController + '.json', json); + self.toggleProperty(targetController + '.showOverlay'); + } + }, function (err) { + self.notify.error(err.responseJSON.message, err.responseJSON.trace); + }); + } else { + this.toggleProperty(targetController + '.showOverlay'); + } } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js index dc75280..e61df51 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js @@ -39,30 +39,44 @@ export default Ember.ObjectController.extend({ if (cachedExplain) { this.formatExplainResults(cachedExplain); } else { - this.getExplain(); + this.getExplain(true); } }.observes('content'), - getExplain: function () { + getExplain: function (firstPage, rows) { var self = this; var url = this.container.lookup('adapter:application').buildURL(); - url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results?first=true'; + url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results'; + + if (firstPage) { + url += '?first=true'; + } Ember.$.getJSON(url).then(function (data) { - var explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({ - id: self.get('content.id'), - explain: data - })); + var explainSet; + + //if rows from a previous page read exist, prepend them + if (rows) { + data.rows.unshiftObjects(rows); + } - self.set('content.explain', explainSet); + if (!data.hasNext) { + explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({ + id: self.get('content.id'), + explain: data + })); - self.formatExplainResults(explainSet); + self.set('content.explain', explainSet); + + self.formatExplainResults(explainSet); + } else { + self.getExplain(false, data.rows); + } }); }, formatExplainResults: function (explainSet) { var formatted = [], - orderedNodes = [], currentNode, currentNodeWhitespace, previousNode, @@ -116,4 +130,4 @@ export default Ember.ObjectController.extend({ this.set('formattedExplain', formatted); } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js index 02edc86..59c0892 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js @@ -28,13 +28,22 @@ export default Ember.ObjectController.extend({ reloadJobLogs: function (job) { var self = this, defer = Ember.RSVP.defer(), - handleError = function (err) { - self.send('addAlert', constants.namingConventions.alerts.error, err.responseText); + handleError = function (error) { + job.set('isRunning', false); + + if (typeof error === "string") { + self.notify.error(error); + } else { + self.notify.error(error.responseJSON.message, error.responseJSON.trace); + } defer.reject(); }; job.reload().then(function () { - self.get('files').reload(job.get('logFile')).then(function (file) { + if (utils.insensitiveCompare(job.get('status'), constants.statuses.error)) { + handleError(job.get('statusMessage')); + } else { + self.get('files').reload(job.get('logFile')).then(function (file) { var fileContent = file.get('fileContent'); if (fileContent) { @@ -42,9 +51,10 @@ export default Ember.ObjectController.extend({ } defer.resolve(); - },function (err) { - handleError(err); - }); + },function (err) { + handleError(err); + }); + } }, function (err) { handleError(err); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js index 7977541..9a50f27 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js @@ -22,6 +22,25 @@ import utils from 'hive/utils/functions'; export default Ember.ObjectController.extend({ cachedResults: [], + formattedResults: [], + + processResults: function() { + var results = this.get('results'); + + if (!results || !results.schema || !results.rows) { + return; + } + + var schema = results.schema.map(function(column) { + return { + name: column[0], + type: column[1], + index: column[2] + } + }); + + this.set('formattedResults', { schema: schema, rows: results.rows }); + }.observes('results'), keepAlive: function (job) { Ember.run.later(this, function () { @@ -73,6 +92,20 @@ export default Ember.ObjectController.extend({ return this.cachedResults.findBy('id', this.get('content.id')).results.indexOf(this.get('results')) <= 0; }.property('results'), + getResultsJson: function (job) { + var defer = Ember.RSVP.defer(); + var url = this.container.lookup('adapter:application').buildURL(); + url += '/' + constants.namingConventions.jobs + '/' + job.get('id') + '/results?first=true'; + + Ember.$.getJSON(url).then(function (results) { + defer.resolve(JSON.parse(results.rows[0][0])); + }, function (err) { + defer.reject(err); + }); + + return defer.promise; + }, + actions: { getNextPage: function (firstPage, job) { var self = this; @@ -115,6 +148,7 @@ export default Ember.ObjectController.extend({ if (firstPage) { self.keepAlive(job || self.get('content')); } + }, function (err) { self.set('error', err.responseText); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js new file mode 100644 index 0000000..6de1c64 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js @@ -0,0 +1,33 @@ +/** +* 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 Ember from 'ember'; + +export default Ember.Controller.extend({ + messages: Ember.computed.alias('notify.messages'), + count: Ember.computed.alias('messages.length'), + + actions: { + removeMessage: function(message) { + this.notify.removeMessage(message); + }, + + removeAllMessages: function() { + this.notify.removeAllMessages(); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js new file mode 100644 index 0000000..d878bc7 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js @@ -0,0 +1,42 @@ +/** + * 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 Ember from 'ember'; +import ModalSave from '../controllers/modal-save'; +import constants from '../utils/constants'; + +export default ModalSave.extend({ + showMessage: function () { + var content = this.get('content'); + + return !content.get('isNew') && + content.get('title') === this.get('text') && + content.get('constructor.typeKey') !== constants.namingConventions.job; + }.property('content.isNew', 'text'), + + actions: { + save: function () { + this.send('closeModal'); + + this.defer.resolve(Ember.Object.create({ + text: this.get('text'), + overwrite: this.get('showMessage') + })); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js index 2abfff6..a0c033e 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js @@ -154,7 +154,7 @@ export default Ember.ArrayController.extend({ return defer.promise; }, - save: function (model, query) { + save: function (model, query, isUpdating, newTitle) { var tab = this.getTabForModel(model), self = this, wasNew, @@ -168,6 +168,7 @@ export default Ember.ArrayController.extend({ if (model.get('isNew')) { wasNew = true; + model.set('title', newTitle); model.set('id', null); } @@ -175,16 +176,30 @@ export default Ember.ArrayController.extend({ if (model.get('constructor.typeKey') === constants.namingConventions.job) { model = this.store.createRecord(constants.namingConventions.savedQuery, { dataBase: this.get('databases.selectedDatabase.name'), - title: model.get('title'), + title: newTitle, queryFile: model.get('queryFile'), owner: model.get('owner') }); + } else { + tab.set('name', newTitle); + } + + //if saving a new query from an existing one create a new record and save it + if (!isUpdating && !model.get('isNew') && model.get('constructor.typeKey') !== constants.namingConventions.job) { + model = this.store.createRecord(constants.namingConventions.savedQuery, { + dataBase: this.get('databases.selectedDatabase.name'), + title: newTitle, + owner: model.get('owner') + }); + + wasNew = true; } model.save().then(function (updatedModel) { - tab.set('name', updatedModel.get('title')); jobModel.set('queryId', updatedModel.get('id')); + tab.set('isDirty', false); + var content = self.get('index').prependQuerySettings(query.get('fileContent')); //update query tab path with saved model id if its a new record if (wasNew) { @@ -198,7 +213,7 @@ export default Ember.ArrayController.extend({ self.pushObject(updatedFile); self.set('currentQuery', updatedFile); - defer.resolve(); + defer.resolve(updatedModel.get('id')); }, function (err) { defer.reject(err); }); @@ -209,7 +224,7 @@ export default Ember.ArrayController.extend({ query.set('fileContent', content); query.save().then(function () { self.toggleProperty('tabUpdated'); - defer.resolve(); + defer.resolve(updatedModel.get('id')); }, function (err) { defer.reject(err); }); @@ -259,34 +274,83 @@ export default Ember.ArrayController.extend({ hasSettings; }, + isDirty: function(model) { + var query = this.getQueryForModel(model); + + if (model.get('isNew') && !query.get('fileContent')) { + return false; + } + + if (query && query.get('isDirty')) { + return true; + } + + return !!(!model.get('queryId') && model.get('isDirty')); + + + }, + + updatedDeletedQueryTab: function (model) { + var tab = this.getTabForModel(model); + + if (tab) { + this.closeTab(tab); + } + }, + + dirtyObserver: function () { + var tab; + var model = this.get('index.model'); + + if (model) { + tab = this.getTabForModel(model); + + if (tab) { + tab.set('isDirty', this.isDirty(model)); + } + } + }.observes('currentQuery.isDirty', 'currentQuery.fileContent'), + + closeTab: function (tab, goToNextTab) { + var remainingTabs = this.get('queryTabs').without(tab); + + this.set('queryTabs', remainingTabs); + + //remove cached results set + if (tab.type === constants.namingConventions.job) { + this.get('jobResults').clearCachedResultsSet(tab.id); + this.get('jobExplain').clearCachedExplainSet(tab.id); + } + + if (goToNextTab) { + this.navigateToLastTab(); + } + }, + + navigateToLastTab: function () { + var lastTab = this.get('queryTabs.lastObject'); + + if (lastTab) { + if (lastTab.type === constants.namingConventions.job) { + this.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id); + } else { + this.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id); + } + } else { + this.get('index').send('addQuery'); + } + }, + actions: { removeQueryTab: function (tab) { var self = this, - defer, - remainingTabs = this.get('queryTabs').without(tab), - lastTab = remainingTabs.get('lastObject'), - closeTab = function () { - self.set('queryTabs', remainingTabs); - - //remove cached results set - if (tab.type === constants.namingConventions.job) { - self.get('jobResults').clearCachedResultsSet(tab.id); - self.get('jobExplain').clearCachedExplainSet(tab.id); - } - - if (lastTab.type === constants.namingConventions.job) { - self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id); - } else { - self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id); - } - }; + defer; this.store.find(tab.type, tab.id).then(function (model) { var query = self.getQueryForModel(model); - if ((model.get('isNew') && !query.get('fileContent')) || - (!model.get('isNew') && !query.get('isDirty'))) { - closeTab(); + if (!self.isDirty(model)) { + self.closeTab(tab, true); } else { defer = Ember.RSVP.defer(); self.send('openModal', @@ -300,11 +364,11 @@ export default Ember.ArrayController.extend({ defer.promise.then(function (text) { model.set('title', text); self.save(model, query).then(function () { - closeTab(); + self.closeTab(tab, true); }); }, function () { model.rollback(); - closeTab(); + self.closeTab(tab, true); }); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js index 0195dc2..aec2273 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js @@ -21,7 +21,11 @@ import FilterableMixin from 'hive/mixins/filterable'; import constants from 'hive/utils/constants'; export default Ember.ArrayController.extend(FilterableMixin, { - needs: [ constants.namingConventions.routes.history ], + needs: [ constants.namingConventions.routes.history, + constants.namingConventions.openQueries ], + + history: Ember.computed.alias('controllers.' + constants.namingConventions.routes.history), + openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries), sortAscending: true, sortProperties: [], @@ -68,9 +72,11 @@ export default Ember.ArrayController.extend(FilterableMixin, { actions: { executeAction: function (action, savedQuery) { + var self = this; + switch (action) { case "buttons.history": - this.get('controllers.' + constants.namingConventions.routes.history).filterBy('queryId', savedQuery.get('id'), true); + this.get('history').filterBy('queryId', savedQuery.get('id'), true); this.transitionToRoute(constants.namingConventions.routes.history); break; case "buttons.delete": @@ -85,6 +91,7 @@ export default Ember.ArrayController.extend(FilterableMixin, { defer.promise.then(function () { savedQuery.destroyRecord(); + self.get('openQueries').updatedDeletedQueryTab(savedQuery); }); break; http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js index 6ac0828..ddc5b1e 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js @@ -27,9 +27,22 @@ export default Ember.ArrayController.extend({ index: Ember.computed.alias('controllers.' + constants.namingConventions.index), openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries), + sessionTag: Ember.computed.alias('index.model.sessionTag'), + sessionActive: Ember.computed.alias('index.model.sessionActive'), + + canInvalidateSession: Ember.computed.and('sessionTag', 'sessionActive'), predefinedSettings: constants.hiveParameters, + selectedSettings: function() { + var predefined = this.get('predefinedSettings'); + var current = this.get('currentSettings.settings'); + + return predefined.filter(function(setting) { + return current.findBy('key.name', setting.name); + }); + }.property('[email protected]'), + currentSettings: function () { var currentId = this.get('index.model.id'); var targetSettings = this.findBy('id', currentId); @@ -150,9 +163,14 @@ export default Ember.ArrayController.extend({ return; } + if (!predefined.validate) { + setting.set('valid', true); + return; + } + setting.set('valid', false); }); - }.observes('currentSettings.[]', '[email protected]', '[email protected]'), + }.observes('currentSettings.[]', 'currentSettings.settings.[]', '[email protected]', '[email protected]'), currentSettingsAreValid: function() { var currentSettings = this.get('currentSettings.settings'); @@ -161,6 +179,24 @@ export default Ember.ArrayController.extend({ return invalid.length ? false : true; }.property('[email protected]', '[email protected]'), + loadSessionStatus: function() { + var model = this.get('index.model'); + var sessionActive = this.get('sessionActive'); + var sessionTag = this.get('sessionTag'); + var adapter = this.container.lookup('adapter:application'); + var url = adapter.buildURL() + '/jobs/sessions/' + sessionTag; + + if (sessionTag && sessionActive === undefined) { + adapter.ajax(url, 'GET') + .then(function(response) { + model.set('sessionActive', response.session.actual); + }) + .catch(function() { + model.set('sessionActive', false); + }); + } + }.observes('index.model', 'index.model.status'), + actions: { add: function () { var currentId = this.get('index.model.id'), @@ -185,6 +221,31 @@ export default Ember.ArrayController.extend({ }); this.get('currentSettings.settings').findBy('key', null).set('key', newKey); + }, + + removeAll: function() { + var currentId = this.get('index.model.id'), + querySettings = this.findBy('id', currentId); + + querySettings.set('settings', []); + }, + + invalidateSession: function() { + var self = this; + var sessionTag = this.get('sessionTag'); + var adapter = this.container.lookup('adapter:application'); + var url = adapter.buildURL() + '/jobs/sessions/' + sessionTag; + var model = this.get('index.model'); + + // @TODO: Split this into then/catch once the BE is fixed + adapter.ajax(url, 'DELETE').catch(function(response) { + if ([200, 404].contains(response.status)) { + model.set('sessionActive', false); + self.notify.success('alerts.success.sessions.deleted'); + } else { + self.notify.error(response.responseJSON.message, response.responseJSON.trace); + } + }); } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js index 657743c..3f8d3ed 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js @@ -50,8 +50,8 @@ export default Ember.ObjectController.extend({ this.send('openModal', 'modal-delete', { - heading: Ember.I18n.translations.modals.delete.heading, - text: Ember.I18n.translations.modals.delete.message, + heading: 'modals.delete.heading', + text: 'modals.delete.message', defer: defer }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js index e5ea321..f068ed0 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js @@ -19,7 +19,7 @@ import Ember from 'ember'; export function allUppercase(input) { - return input.toUpperCase(); + return input ? input.toUpperCase() : input; }; export default Ember.Handlebars.makeBoundHelper(allUppercase); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js new file mode 100644 index 0000000..bb3d006 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js @@ -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 Ember from 'ember'; + +export function preformattedString(string) { + string = string.replace(/\\n/g, ' '); // newline + string = string.replace(/\\t/g, '	'); // tabs + string = string.replace(/^\s+|\s+$/g, ''); // trim + + return new Ember.Handlebars.SafeString(string); +} + +export default Ember.Handlebars.makeBoundHelper(preformattedString); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html index 4f609bd..2cbf9f0 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html @@ -28,11 +28,15 @@ <link rel="stylesheet" href="assets/vendor.css"> <link rel="stylesheet" href="assets/hive.css"> + + {{content-for 'head-footer'}} </head> <body> {{content-for 'body'}} <script src="assets/vendor.js"></script> <script src="assets/hive.js"></script> + + {{content-for 'body-footer'}} </body> </html> http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js index 8d60248..b637c5e 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js @@ -28,9 +28,13 @@ export default { Ember.I18n.translations = TRANSLATIONS; Ember.TextField.reopen(Ember.I18n.TranslateableAttributes); } -}; +} TRANSLATIONS = { + tooltips: { + refresh: 'Refresh database', + loadSample: 'Load sample data' + }, alerts: { errors: { save: { @@ -40,6 +44,17 @@ TRANSLATIONS = { get: { tables: 'Error when trying to retrieve the tables for the selected database', columns: 'Error when trying to retrieve the table columns.' + }, + sessions: { + delete: 'Error invalidating sessions' + }, + job: { + status: "An error occured while processing the job." + } + }, + success: { + sessions: { + deleted: 'Session invalidated' } } }, @@ -53,7 +68,12 @@ TRANSLATIONS = { save: { heading: 'Saving item', saveBeforeCloseHeading: "Save item before closing?", - message: 'Enter name:' + message: 'Enter name:', + overwrite: 'Saving will overwrite previously saved query' + }, + + download: { + csv: 'Download results as CSV' } }, titles: { @@ -62,13 +82,15 @@ TRANSLATIONS = { results: 'Search Results', settings: 'Database Settings', query: { + tab: 'Worksheet', editor: 'Query Editor', process: 'Query Process Results', parameters: 'Parameters', visualExplain: 'Visual Explain', tez: 'TEZ' }, - download: 'Save results...' + download: 'Save results...', + tableSample: '{{tableName}} sample' }, placeholders: { search: { @@ -130,7 +152,7 @@ TRANSLATIONS = { explain: 'Explain', saveAs: 'Save as...', save: 'Save', - newQuery: 'New Query', + newQuery: 'New Worksheet', newUdf: 'New UDF', history: 'History', ok: 'OK', @@ -147,9 +169,13 @@ TRANSLATIONS = { runOnTez: 'Run on Tez' }, labels: { - noTablesMatches: 'No tables matches for' + noTablesMatch: 'No tables match', + table: 'Table ' }, popover: { + visualExplain: { + statistics: "Statistics" + }, queryEditorHelp: { title: "Did you know?", content: { @@ -163,7 +189,10 @@ TRANSLATIONS = { tez: { errors: { 'not.deployed': "Tez View isn't deployed.", - 'no.instance': "No instance of Tez View found." + 'no.instance': "No instance of Tez View found.", + 'no.dag': "No DAG available" } - } + }, + + generalError: 'Unexpected error' }; http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js new file mode 100644 index 0000000..be9c359 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js @@ -0,0 +1,26 @@ +/** +* 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 { + name: 'notify', + initialize: function(container, app) { + app.inject('route', 'notify', 'service:notify'); + app.inject('controller', 'notify', 'service:notify'); + app.inject('component', 'notify', 'service:notify'); + app.inject('views', 'notify', 'service:notify'); + } +}; http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js index f8f1b9b..c13d4e1 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js @@ -21,6 +21,6 @@ import DS from 'ember-data'; export default DS.Model.extend({ fileContent: DS.attr(), hasNext: DS.attr(), - page: DS.attr, + page: DS.attr('number'), pageCount: DS.attr() }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js index 06bdc2e..472f824 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js @@ -26,12 +26,17 @@ export default DS.Model.extend({ dataBase: DS.attr('string'), duration: DS.attr(), status: DS.attr('string'), + statusMessage: DS.attr('string'), dateSubmitted: DS.attr('date'), forcedContent: DS.attr('string'), logFile: DS.attr('string'), dagName: DS.attr('string'), dagId: DS.attr('string'), sessionTag: DS.attr('string'), + page: DS.attr(), + statusDir: DS.attr('string'), + applicationId: DS.attr(), + confFile: DS.attr('string'), dateSubmittedTimestamp: function () { var date = this.get('dateSubmitted'); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js index 2f9a5ae..bf413a3 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js @@ -33,6 +33,8 @@ export default Ember.Route.extend({ actions: { openModal: function (modalTemplate, options) { this.controllerFor(modalTemplate).setProperties({ + content: options.content || {}, + message: options.message, heading: options.heading, text: options.text, defer: options.defer @@ -51,11 +53,16 @@ export default Ember.Route.extend({ }); }, - addAlert: function (type, message, title) { - this.controllerFor(constants.namingConventions.alerts).pushObject({ - type: type, - title: title, - content: message + openOverlay: function(overlay) { + return this.render(overlay.template, { + outlet: overlay.outlet, + into: overlay.into + }); + }, + closeOverlay: function(overlay) { + return this.disconnectOutlet({ + outlet: overlay.outlet, + parentView: overlay.into }); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js index f2be946..120a102 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js @@ -23,14 +23,14 @@ export default Ember.Route.extend({ beforeModel: function () { var model = this.controllerFor(constants.namingConventions.routes.index).get('model'); - if (model) { + if (model && !model.get('isDeleted')) { if (model.get('constructor.typeKey') === constants.namingConventions.job) { this.transitionTo(constants.namingConventions.subroutes.historyQuery, model); } else { this.transitionTo(constants.namingConventions.subroutes.savedQuery, model); } } else { - this.controllerFor(constants.namingConventions.routes.index).send('addQuery'); + this.controllerFor(constants.namingConventions.openQueries).navigateToLastTab(); } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js new file mode 100644 index 0000000..fbd50cd --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js @@ -0,0 +1,88 @@ +/** +* 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 Ember from 'ember'; +import constants from 'hive/utils/constants'; + +export default Ember.Service.extend({ + types: constants.notify, + + messages : Ember.ArrayProxy.create({ content : [] }), + notifications : Ember.ArrayProxy.create({ content : [] }), + + add: function(type, message, body) { + var formattedBody = this.formatMessageBody(body); + + var notification = Ember.Object.create({ + type : type, + message : message, + body : formattedBody + }); + + this.messages.pushObject(notification); + this.notifications.pushObject(notification); + }, + + info: function(message, body) { + this.add(this.types.INFO, message, body); + }, + + warn: function(message, body) { + this.add(this.types.WARN, message, body); + }, + + error: function(message, body) { + this.add(this.types.ERROR, message, body); + }, + + success: function(message, body) { + this.add(this.types.SUCCESS, message, body); + }, + + formatMessageBody: function(body) { + if (!body) { + return; + } + + if (typeof body === "string") { + return body; + } + + if (typeof body === "object") { + var formattedBody = ""; + for (var key in body) { + formattedBody += "\n\n%@:\n%@".fmt(key, body[key]); + } + + return formattedBody; + } + }, + + removeMessage: function(message) { + this.messages.removeObject(message); + this.notifications.removeObject(message); + }, + + removeNotification: function(notification) { + this.notifications.removeObject(notification); + }, + + removeAllMessages: function() { + this.messages.removeAt(0, this.messages.get('length')); + } + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss index 3f49713..7fdf096 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss @@ -16,11 +16,15 @@ * limitations under the License. */ +@import 'vars'; @import 'dropdown-submenu'; +@import 'mixins'; +@import 'notifications'; +@import 'query-tabs'; -$panel-background: #f5f5f5; -$placeholder-color: #aaa; -$border-color: #ddd; +a { + word-wrap: break-word; +} @-webkit-keyframes fadeIn { 0% {opacity: 0;} @@ -60,6 +64,10 @@ $border-color: #ddd; display: flex; } +#visual-explain { + white-space: nowrap; +} + #visual-explain, #tez-ui { position: absolute; left: 0; @@ -68,13 +76,6 @@ $border-color: #ddd; background: white; } -#alerts-container { - position: absolute; - left: 15px; - right: 15px; - z-index: 1100; -} - aside hr { margin: 10px 0; } @@ -181,21 +182,10 @@ dropdown .fa-remove { } .main-content { + width: 90%; flex-grow: 1; } -.query-menu { - margin-top: 57px; - - span, popover { - cursor: pointer; - overflow: hidden; - display: block; - border-bottom: 1px solid $border-color; - padding: 10px; - } -} - .queries-icon { font-size: 20px; @@ -209,11 +199,23 @@ dropdown .fa-remove { } } +.query-context-tab { + background: #f1f1f1; + + &.active { + background: white; + } +} + .alert { margin-bottom: 5px; padding-bottom: 10px; padding-top: 10px; + strong { + text-decoration: underline; + } + .alert-message { max-height: 250px; overflow-y: auto; @@ -317,22 +319,8 @@ body { } .settings-container { - width: 100%; - overflow-y: scroll; - height: calc(100% - 41px); - top: 41px; - left: 0; - background-color: #fff; - position: absolute; - padding: 0 15px; - z-index: 1000; - - border: 1px solid $border-color; - -webkit-animation-duration: .5s; - animation-duration: .5s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; } + .settings-container .close-settings { float: right; font-size: 18px; @@ -377,3 +365,77 @@ tree-view ul li { height: 822px; border: none; } + +.edge { + text-align: center; + font-size: 10px; + font-weight: 800; + + .edge-path { + height: 2px; + background-color: #dedede; + position: absolute; + } + + .edge-arrow { + position: absolute; + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 10px solid black; + } +} + +.nodes { + width: 100%; + position: relative; + + .node-container { + text-align: center; + + .node { + border: 1px solid #bbb; + background: #fefefe; + font-size: 12px; + box-sizing: border-box; + text-align: left; + max-width: 200px; + margin: 0 25px 100px 0; + display: inline-block; + vertical-align: top; + + @include box-shadow(1px, 1px, 15px, #888888); + + &.table-node, &.output-node { + background-color: ghostwhite; + color: gray; + padding: 5px; + text-align: center; + min-width: 100px; + line-height: 8px; + vertical-align: bottom; + margin-bottom: 50px; + } + + .node-heading { + padding: 5px; + text-align: center; + background-color: lightslategrey; + color: white; + } + + .node-content { + max-height: 300px; + white-space: normal; + padding: 5px; + overflow-y: auto; + overflow-x: hidden; + + .fa { + color: green; + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss new file mode 100644 index 0000000..95e4ae8 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss @@ -0,0 +1,23 @@ +/** + * 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. + */ + +@mixin box-shadow($horizontal, $vertical, $blur, $color) { + -webkit-box-shadow: $horizontal $vertical $blur $color; + -moz-box-shadow: $horizontal $vertical $blur $color; + box-shadow: $horizontal $vertical $blur $color; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss new file mode 100644 index 0000000..c676e4e --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss @@ -0,0 +1,36 @@ +/** +* 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. +*/ +.notifications-container { + position: absolute; + top: 4px; + right: 20px; + width: 600px; + z-index: 9999; +} + +.notification > .fa { + width: 15px; + text-align: center; + margin-right: 10px; +} + +.notifications-container .notification { + word-wrap: break-word; + max-height: 200px; + overflow-x: auto; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss new file mode 100644 index 0000000..d23a751 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss @@ -0,0 +1,68 @@ +/** +* 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. +*/ +.query-menu { + margin-top: 57px; + + span, popover { + cursor: pointer; + display: block; + border-bottom: 1px solid $border-color; + padding: 10px; + } +} + +.fa.panel-action-icon { + line-height: 22px; + font-size: 16px; +} + +.editor-overlay { + width: 100%; + overflow-y: scroll; + height: calc(100% - 41px); + top: 41px; + left: 0; + background-color: #fff; + position: absolute; + padding: 0 15px; + z-index: 1000; + + border: 1px solid $border-color; + -webkit-animation-duration: .5s; + animation-duration: .5s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.message-body { + margin-top: 10px; +} + +.query-menu-tab { + position: relative; +} +.query-menu-tab .badge { + position: absolute; + top: -4px; + left: -4px; + background-color: red; + color: #fff; + padding: 2px 4px; + font-weight: bold; + z-index: 9999; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss new file mode 100644 index 0000000..eec2277 --- /dev/null +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss @@ -0,0 +1,20 @@ +/** +* 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. +*/ +$panel-background: #f5f5f5; +$placeholder-color: #aaa; +$border-color: #ddd; http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs deleted file mode 100644 index 0fbf34a..0000000 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs +++ /dev/null @@ -1,23 +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. -}} - -<div id="alerts-container"> - {{#each alert in this}} - {{alert-message-widget message=alert removeMessage="remove" removeLater="removeLater"}} - {{/each}} -</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs index 055a87b..99662dd 100644 --- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs +++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs @@ -16,10 +16,11 @@ * limitations under the License. }} +{{notify-widget}} {{render 'navbar'}} <div id="content"> {{outlet}} {{outlet "modal"}} -</div> \ No newline at end of file +</div>
