Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 60816200C05 for ; Mon, 9 Jan 2017 07:14:05 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 5F240160B45; Mon, 9 Jan 2017 06:14:05 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id AF682160B36 for ; Mon, 9 Jan 2017 07:14:03 +0100 (CET) Received: (qmail 48313 invoked by uid 500); 9 Jan 2017 06:14:02 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 48303 invoked by uid 99); 9 Jan 2017 06:14:01 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 09 Jan 2017 06:14:01 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 78841DFA0E; Mon, 9 Jan 2017 06:14:01 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: dbhowmick@apache.org To: commits@ambari.apache.org Message-Id: <71411160a19144dead7d13d8837796b1@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: ambari git commit: AMBARI-19379. Hive View 2.0: Ability to delete a table from UI. (dipayanb) Date: Mon, 9 Jan 2017 06:14:01 +0000 (UTC) archived-at: Mon, 09 Jan 2017 06:14:05 -0000 Repository: ambari Updated Branches: refs/heads/branch-2.5 d346558c1 -> 1e2fe429b AMBARI-19379. Hive View 2.0: Ability to delete a table from UI. (dipayanb) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/1e2fe429 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/1e2fe429 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/1e2fe429 Branch: refs/heads/branch-2.5 Commit: 1e2fe429b88e1963d1cf40285dd5cf248fc556fd Parents: d346558 Author: Dipayan Bhowmick Authored: Mon Jan 9 11:43:36 2017 +0530 Committer: Dipayan Bhowmick Committed: Mon Jan 9 11:43:36 2017 +0530 ---------------------------------------------------------------------- .../ambari/view/hive20/ConnectionSystem.java | 4 + .../view/hive20/actor/DatabaseManager.java | 44 ++++- .../ambari/view/hive20/actor/JdbcConnector.java | 6 + .../view/hive20/actor/MetaDataManager.java | 3 + .../view/hive20/actor/MetaDataRetriever.java | 12 +- .../ambari/view/hive20/actor/message/Ping.java | 10 + .../hive20/resources/browser/DDLService.java | 4 +- .../utils/MetaDataManagerEventSubmitter.java | 43 +++++ .../src/main/resources/ui/app/adapters/table.js | 7 +- .../ui/app/routes/databases/database/tables.js | 5 + .../app/routes/databases/database/tables/new.js | 25 ++- .../routes/databases/database/tables/table.js | 53 ++++- .../resources/ui/app/services/create-table.js | 182 ----------------- .../ui/app/services/table-operations.js | 193 +++++++++++++++++++ .../app/templates/components/jobs-browser.hbs | 2 +- .../databases/database/tables/table.hbs | 20 +- .../databases/database/tables/table/details.hbs | 2 +- .../databases/database/tables/table/storage.hbs | 2 +- 18 files changed, 411 insertions(+), 206 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/ConnectionSystem.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/ConnectionSystem.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/ConnectionSystem.java index 40b4c04..a6c7334 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/ConnectionSystem.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/ConnectionSystem.java @@ -124,6 +124,10 @@ public class ConnectionSystem { return metaDataManager; } + public synchronized Optional getMetaDataManagerIfPresent(String instanceName) { + return Optional.fromNullable(metaDataManagerMap.get(instanceName)); + } + private ActorRef createMetaDataManager(SafeViewContext safeViewContext) { return actorSystem.actorOf(MetaDataManager.props(safeViewContext)); } http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/DatabaseManager.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/DatabaseManager.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/DatabaseManager.java index 6dc4ad9..bd7c6bd 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/DatabaseManager.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/DatabaseManager.java @@ -75,7 +75,7 @@ public class DatabaseManager extends HiveActor { Object message = hiveMessage.getMessage(); if (message instanceof Refresh) { - handleRefresh(); + handleRefresh((Refresh) message); } else if (message instanceof SelfRefresh) { handleSelfRefresh(); } else if (message instanceof MetaDataRetriever.DBRefreshed) { @@ -83,7 +83,7 @@ public class DatabaseManager extends HiveActor { } else if (message instanceof MetaDataRetriever.TableRefreshed) { handleTableRefreshed((MetaDataRetriever.TableRefreshed) message); } else if (message instanceof MetaDataRetriever.AllTableRefreshed) { - handleAllTableRefeshed((MetaDataRetriever.AllTableRefreshed) message); + handleAllTableRefreshed((MetaDataRetriever.AllTableRefreshed) message); } else if (message instanceof GetDatabases) { handleGetDatabases((GetDatabases) message); } @@ -96,11 +96,11 @@ public class DatabaseManager extends HiveActor { getSelf(), new SelfRefresh(), getContext().dispatcher(), getSelf()); } else { selfRefreshQueued = false; - refresh(); + refresh(true); } } - private void handleRefresh() { + private void handleRefresh(Refresh message) { if (refreshInProgress && selfRefreshQueued) { return; // We will not honor refresh message when a refresh is going on and another self refresh is queued in mailbox } else if (refreshInProgress) { @@ -108,7 +108,7 @@ public class DatabaseManager extends HiveActor { getContext().system().scheduler().scheduleOnce(Duration.create(500, TimeUnit.MILLISECONDS), getSelf(), new SelfRefresh(), getContext().dispatcher(), getSelf()); } else { - refresh(); + refresh(message.initiateScheduler()); } } @@ -157,8 +157,9 @@ public class DatabaseManager extends HiveActor { databaseChangeNotifier.tell(new DatabaseChangeNotifier.TableUpdated(message.getTable()), getSelf()); } - private void handleAllTableRefeshed(MetaDataRetriever.AllTableRefreshed message) { + private void handleAllTableRefreshed(MetaDataRetriever.AllTableRefreshed message) { ActorRef databaseChangeNotifier = getDatabaseChangeNotifier(message.getDatabase()); + updateRemovedTables(message.getDatabase(), message.getCurrentTableNames()); databaseChangeNotifier.tell(new DatabaseChangeNotifier.AllTablesUpdated(message.getDatabase()), getSelf()); if (checkIfAllTablesOfAllDatabaseRefeshed(message)) { refreshInProgress = false; @@ -193,12 +194,14 @@ public class DatabaseManager extends HiveActor { return databaseChangeNotifier; } - private void refresh() { + private void refresh(boolean initiateScheduler) { LOG.info("Received refresh for user"); refreshInProgress = true; metaDataRetriever.tell(new MetaDataRetriever.RefreshDB(), getSelf()); - scheduleRefreshAfter(1, TimeUnit.MINUTES); + if (initiateScheduler) { + scheduleRefreshAfter(1, TimeUnit.MINUTES); + } } private void scheduleRefreshAfter(long time, TimeUnit timeUnit) { @@ -220,6 +223,20 @@ public class DatabaseManager extends HiveActor { } } + private void updateRemovedTables(String database, Set currentTableNames) { + DatabaseWrapper wrapper = databases.get(database); + HashSet notRemovedTables = new HashSet<>(); + if (wrapper != null) { + DatabaseInfo info = wrapper.getDatabase(); + for (TableInfo tableInfo : info.getTables()) { + if (currentTableNames.contains(tableInfo.getName())) { + notRemovedTables.add(tableInfo); + } + } + info.setTables(notRemovedTables); + } + } + public static Props props(ViewContext context) { ConnectionConfig config = ConnectionFactory.create(context); Connectable connectable = new HiveConnectionWrapper(config.getJdbcUrl(), config.getUsername(), config.getPassword(), new AuthParams(context)); @@ -228,14 +245,25 @@ public class DatabaseManager extends HiveActor { public static class Refresh { private final String username; + private final boolean initiateScheduler; + public Refresh(String username) { + this(username, true); + } + + public Refresh(String username, boolean initiateScheduler) { this.username = username; + this.initiateScheduler = initiateScheduler; } public String getUsername() { return username; } + + public boolean initiateScheduler() { + return initiateScheduler; + } } private static class SelfRefresh { http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/JdbcConnector.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/JdbcConnector.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/JdbcConnector.java index ce58c8c..1855afc 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/JdbcConnector.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/JdbcConnector.java @@ -57,6 +57,7 @@ import org.apache.ambari.view.hive20.persistence.utils.ItemNotFound; import org.apache.ambari.view.hive20.resources.jobs.viewJobs.Job; import org.apache.ambari.view.hive20.resources.jobs.viewJobs.JobImpl; import org.apache.ambari.view.hive20.utils.HiveActorConfiguration; +import org.apache.ambari.view.hive20.utils.MetaDataManagerEventSubmitter; import org.apache.ambari.view.utils.hdfs.HdfsApi; import org.apache.hive.jdbc.HiveConnection; import org.slf4j.Logger; @@ -132,6 +133,7 @@ public class JdbcConnector extends HiveActor { */ private final HiveActorConfiguration actorConfiguration; private String username; + private String instanceName; private Optional jobId = Optional.absent(); private Optional logFile = Optional.absent(); private int statementsCount = 0; @@ -157,6 +159,7 @@ public class JdbcConnector extends HiveActor { this.storage = storage; this.lastActivityTimestamp = System.currentTimeMillis(); resultSetIterator = null; + this.instanceName = viewContext.getInstanceName(); authParams = new AuthParams(viewContext); actorConfiguration = new HiveActorConfiguration(viewContext); @@ -287,6 +290,9 @@ public class JdbcConnector extends HiveActor { LOG.info("Finished processing SQL statements for Job id : {}", jobId.or("SYNC JOB")); if (isAsync() && jobId.isPresent()) { updateJobStatus(jobId.get(), Job.JOB_STATE_FINISHED); + + LOG.info("Sending event to refresh meta information for user {} and instance {}", username, instanceName); + MetaDataManagerEventSubmitter.sendDBRefresh(username, instanceName); } if (resultSetOptional.isPresent()) { http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataManager.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataManager.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataManager.java index d63b3a0..43733e4 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataManager.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataManager.java @@ -72,6 +72,9 @@ public class MetaDataManager extends HiveActor { databaseManagers.put(context.getUsername(), databaseManager); databaseManager.tell(new DatabaseManager.Refresh(context.getUsername()), getSelf()); } else { + if(message.isImmediate()) { + databaseManager.tell(new DatabaseManager.Refresh(context.getUsername(), false), getSelf()); + } cancelTerminationScheduler(message.getUsername()); } scheduleTermination(context.getUsername()); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataRetriever.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataRetriever.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataRetriever.java index 7323a0a..64cd69c 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataRetriever.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/MetaDataRetriever.java @@ -91,13 +91,15 @@ public class MetaDataRetriever extends HiveActor { private void refreshTablesInfo(String database) throws ConnectionException, SQLException { HiveConnection connection = getHiveConnection(); + Set currentTableNames = new HashSet<>(); try (ResultSet tables = connection.getMetaData().getTables("", database, null, null)) { while (tables.next()) { TableInfo info = new TableInfo(tables.getString(3), tables.getString(4)); + currentTableNames.add(info.getName()); getSender().tell(new TableRefreshed(info, database), getSelf()); } } - getSender().tell(new AllTableRefreshed(database), getSelf()); + getSender().tell(new AllTableRefreshed(database, currentTableNames), getSelf()); } public static Props props(Connectable connectable) { @@ -153,13 +155,19 @@ public class MetaDataRetriever extends HiveActor { public static class AllTableRefreshed { private final String database; + private final Set currentTableNames; - public AllTableRefreshed(String database) { + public AllTableRefreshed(String database, Set currentTableNames) { this.database = database; + this.currentTableNames = currentTableNames; } public String getDatabase() { return database; } + + public Set getCurrentTableNames() { + return currentTableNames; + } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/message/Ping.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/message/Ping.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/message/Ping.java index c8449dc..61df87a 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/message/Ping.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/actor/message/Ping.java @@ -24,10 +24,16 @@ package org.apache.ambari.view.hive20.actor.message; public class Ping { private final String username; private final String instanceName; + private final boolean immediate; public Ping(String username, String instanceName) { + this(username, instanceName, false); + } + + public Ping(String username, String instanceName, boolean immediate) { this.username = username; this.instanceName = instanceName; + this.immediate = immediate; } public String getUsername() { @@ -37,4 +43,8 @@ public class Ping { public String getInstanceName() { return instanceName; } + + public boolean isImmediate() { + return immediate; + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/resources/browser/DDLService.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/resources/browser/DDLService.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/resources/browser/DDLService.java index b278983..65647ee 100644 --- a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/resources/browser/DDLService.java +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/resources/browser/DDLService.java @@ -114,7 +114,7 @@ public class DDLService extends BaseService { Job job = proxy.createTable(databaseName, request.tableInfo, getResourceManager()); JSONObject response = new JSONObject(); response.put("job", job); - return Response.status(Response.Status.ACCEPTED).entity(job).build(); + return Response.status(Response.Status.ACCEPTED).entity(response).build(); } catch (ServiceException e) { LOG.error("Exception occurred while creatint table for db {} with details : {}", databaseName, request.tableInfo, e); throw new ServiceFormattedException(e); @@ -192,7 +192,7 @@ public class DDLService extends BaseService { Job job = proxy.alterTable(context, hiveConnectionConfig, databaseName, oldTableName, tableMetaRequest.tableInfo, getResourceManager()); JSONObject response = new JSONObject(); response.put("job", job); - return Response.status(Response.Status.ACCEPTED).entity(job).build(); + return Response.status(Response.Status.ACCEPTED).entity(response).build(); } catch (ServiceException e) { LOG.error("Exception occurred while creatint table for db {} with details : {}", databaseName, tableMetaRequest.tableInfo, e); throw new ServiceFormattedException(e); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/utils/MetaDataManagerEventSubmitter.java ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/utils/MetaDataManagerEventSubmitter.java b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/utils/MetaDataManagerEventSubmitter.java new file mode 100644 index 0000000..b23e06e --- /dev/null +++ b/contrib/views/hive20/src/main/java/org/apache/ambari/view/hive20/utils/MetaDataManagerEventSubmitter.java @@ -0,0 +1,43 @@ +/** + * 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. + */ + +package org.apache.ambari.view.hive20.utils; + +import akka.actor.ActorRef; +import com.google.common.base.Optional; +import org.apache.ambari.view.hive20.ConnectionSystem; +import org.apache.ambari.view.hive20.actor.message.Ping; + +/** + * Static class to submit event to the MetaData Manager. + */ +public final class MetaDataManagerEventSubmitter { + + /** + * Send a ping message to the MetaDataManager Actor for that instance + * @param username Logged-in username + * @param instanceName current instance name + */ + public static void sendDBRefresh(String username, String instanceName) { + Optional metaDataManagerOptional = ConnectionSystem.getInstance().getMetaDataManagerIfPresent(instanceName); + if(metaDataManagerOptional.isPresent()) { + ActorRef metaDataManager = metaDataManagerOptional.get(); + metaDataManager.tell(new Ping(username, instanceName, true), ActorRef.noSender()); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js b/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js index f4d1615..9a4692d 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js +++ b/contrib/views/hive20/src/main/resources/ui/app/adapters/table.js @@ -41,8 +41,11 @@ export default DDLAdapter.extend({ createTable(tableMetaInfo) { let postURL = this.buildURL('table', null, null, 'query', {databaseId: tableMetaInfo.database}); - console.log(postURL); - console.log(tableMetaInfo); return this.ajax(postURL, 'POST', { data: {tableInfo: tableMetaInfo} }); + }, + + deleteTable(database, tableName) { + let deletURL = this.buildURL('table', null, null, 'query', {databaseId: database, tableName: tableName}); + return this.ajax(deletURL, 'DELETE'); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js index a121b88..92f483f 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables.js @@ -44,4 +44,9 @@ export default Ember.Route.extend({ this.transitionTo('databases.database.tables.table', table.get('name')); } } + + + + + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js index f648ef6..6dfdf29 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/new.js @@ -20,7 +20,7 @@ import Ember from 'ember'; import tabs from '../../../../configs/create-table-tabs'; export default Ember.Route.extend({ - createTable: Ember.inject.service(), + tableOperations: Ember.inject.service(), setupController(controller, model) { this._super(controller, model); @@ -34,20 +34,21 @@ export default Ember.Route.extend({ }, create(settings) { - debugger; this.controller.set('showCreateTableModal', true); this.controller.set('createTableMessage', 'Submitting request to create table'); let databaseModel = this.controllerFor('databases.database').get('model'); - this.get('createTable').submitCreateTable(databaseModel.get('name'), settings) + this.get('tableOperations').submitCreateTable(databaseModel.get('name'), settings) .then((job) => { console.log('Created job: ', job.get('id')); this.controller.set('createTableMessage', 'Waiting for the table to be created'); - this.get('createTable').waitForJobToComplete(job.get('id'), 5 * 1000) + this.get('tableOperations').waitForJobToComplete(job.get('id'), 5 * 1000) .then((status) => { this.controller.set('createTableMessage', "Successfully created table"); Ember.run.later(() => { this.controller.set('showCreateTableModal', false); this.controller.set('createTableMessage'); + this._addTableToStoreLocally(databaseModel, settings.name); + this._resetModelInTablesController(databaseModel.get('tables')); this._transitionToCreatedTable(databaseModel.get('name'), settings.name); }, 2 * 1000); }, (error) => { @@ -67,5 +68,21 @@ export default Ember.Route.extend({ _transitionToCreatedTable(database, table) { this.transitionTo('databases.database.tables.table', database, table); + }, + + _addTableToStoreLocally(database, table) { + this.store.createRecord('table', { + id: `${database.get('name')}/${table}`, + name: `${table}`, + type: 'TABLE', + selected: true, + database: database + }); + }, + + _resetModelInTablesController(tables) { + let tablesController = this.controllerFor('databases.database.tables'); + tablesController.get('model').setEach('selected', false); + tablesController.set('model', tables); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js index 5a88bd2..88f1e7e 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js +++ b/contrib/views/hive20/src/main/resources/ui/app/routes/databases/database/tables/table.js @@ -20,6 +20,7 @@ import Ember from 'ember'; import tabs from '../../../../configs/table-level-tabs'; export default Ember.Route.extend({ + tableOperations: Ember.inject.service(), model(params) { let database = this.modelFor('databases.database').get('name'); let table = params.name; @@ -40,5 +41,55 @@ export default Ember.Route.extend({ controller.set('tabs', newTabs); }, - actions: {} + actions: { + deleteTable(table) { + this.deleteTable(table); + }, + + editTable(table) { + console.log("Edit table"); + } + }, + + deleteTable(tableInfo) { + this.controller.set('showDeleteTableModal', true); + this.controller.set('deleteTableMessage', 'Submitting request to delete table'); + let databaseModel = this.controllerFor('databases.database').get('model'); + this.get('tableOperations').deleteTable(databaseModel.get('name'), tableInfo.get('table')) + .then((job) => { + this.controller.set('deleteTableMessage', 'Waiting for the table to be deleted'); + this.get('tableOperations').waitForJobToComplete(job.get('id'), 5 * 1000) + .then((status) => { + this.controller.set('deleteTableMessage', "Successfully Deleted table"); + Ember.run.later(() => { + this.controller.set('showDeleteTableModal', false); + this.controller.set('deleteTableMessage'); + this._removeTableLocally(databaseModel.get('name'), tableInfo.get('table')); + this._resetModelInTablesController(databaseModel.get('name'), tableInfo.get('table')); + this.transitionTo('databases.database', databaseModel.get('name')); + }, 2 * 1000); + }, (error) => { + // TODO: handle error + Ember.run.later(() => { + this.controller.set('showDeleteTableModal', false); + this.controller.set('deleteTableMessage'); + this.transitionTo('databases.database', databaseModel.get('name')); + }, 2 * 1000); + }); + }, (error) => { + console.log("Error encountered", error); + this.controller.set('showDeleteTableModal', true); + }); + }, + + _removeTableLocally(database, table) { + let tableToBeRemoved = this.store.peekRecord('table', `${database}/${table}`); + this.store.deleteRecord(tableToBeRemoved); + }, + + _resetModelInTablesController(database, tables) { + let tablesController = this.controllerFor('databases.database.tables'); + let currentTables = this.store.peekRecord('database', database).get('tables'); + tablesController.set('model', currentTables); + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/services/create-table.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/services/create-table.js b/contrib/views/hive20/src/main/resources/ui/app/services/create-table.js deleted file mode 100644 index 135f96a..0000000 --- a/contrib/views/hive20/src/main/resources/ui/app/services/create-table.js +++ /dev/null @@ -1,182 +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.Service.extend({ - store: Ember.inject.service(), - - submitCreateTable(database, settings) { - let detailedInfo = this._getDetailedInfo(settings); - let storageInfo = this._getStorageInfo(settings); - let columns = this._getColumns(settings); - let partitionColumns = this._getPartitionColumns(settings); - - let tableInfo = Ember.Object.create({ - database: database, - table: settings.name, - columns: columns, - partitionInfo: { columns: partitionColumns }, - detailedInfo: detailedInfo, - storageInfo: storageInfo - }); - return new Promise((resolve, reject) => { - this.get('store').adapterFor('table').createTable(tableInfo).then((data) => { - this.get('store').pushPayload({job: data}); - resolve(this.get('store').peekRecord('job', data.id)); - }, (err) => { - reject(err); - }); - }); - }, - - waitForJobToComplete(jobId, after) { - return new Ember.RSVP.Promise((resolve, reject) => { - Ember.run.later(() => { - this.get('store').findRecord('job', jobId, {reload: true}) - .then((job) => { - let status = job.get('status').toLowerCase(); - if (status === 'succeeded') { - this._fetchDummyResult(jobId); - resolve(); - } else if (status === 'error') { - reject() - } else { - resolve(this.waitForJobToComplete(jobId, after)); - } - }, (error) => { - reject(error); - }); - }, after); - }); - }, - - _fetchDummyResult(jobId) { - this.get('store').adapterFor('job').fetchResult(jobId); - }, - - _getDetailedInfo(settings) { - let detailedInfo = {}; - detailedInfo['parameters'] = this._getTableProperties(settings); - - if (!Ember.isEmpty(settings.settings.location)) { - detailedInfo['location'] = settings.settings.location; - } - - return detailedInfo; - - }, - - _getStorageInfo(settings) { - const storageSettings = settings.settings; - let storageInfo = {}; - let parameters = {}; - - - - if (!(Ember.isEmpty(storageSettings.fileFormat) || Ember.isEmpty(storageSettings.fileFormat.type))) { - storageInfo.fileFormat = storageSettings.fileFormat.type; - if (storageSettings.fileFormat.type === 'CUSTOM Serde') { - storageInfo.inputFormat = storageSettings.inputFormat; - storageInfo.outputFormat = storageSettings.outputFormat; - } - } - - if (!Ember.isEmpty(storageSettings.rowFormat)) { - let addParameters = false; - if (!Ember.isEmpty(storageSettings.rowFormat.fieldTerminatedBy)) { - parameters['field.delim'] = String.fromCharCode(storageSettings.rowFormat.fieldTerminatedBy.id); - addParameters = true; - } - - if (!Ember.isEmpty(storageSettings.rowFormat.linesTerminatedBy)) { - parameters['line.delim'] = String.fromCharCode(storageSettings.rowFormat.linesTerminatedBy.id); - addParameters = true; - } - - if (!Ember.isEmpty(storageSettings.rowFormat.nullDefinedAs)) { - parameters['serialization.null.format'] = String.fromCharCode(storageSettings.rowFormat.fieldTerminatedBy.id); - addParameters = true; - } - - if (!Ember.isEmpty(storageSettings.rowFormat.escapeDefinedAs)) { - parameters['escape.delim'] = String.fromCharCode(storageSettings.rowFormat.linesTerminatedBy.id); - addParameters = true; - } - - if (addParameters) { - storageInfo.parameters = parameters; - } - } - - if (!Ember.isEmpty(settings.settings.numBuckets)) { - storageInfo['numBuckets'] = settings.settings.numBuckets; - } - - let clusteredColumnNames = settings.columns.filterBy('isClustered', true).map((column) => { - return column.get('name'); - }); - - if (clusteredColumnNames.length > 0) { - storageInfo['bucketCols'] = clusteredColumnNames; - } - - return storageInfo; - }, - - _getColumns(settings) { - return settings.columns.filterBy('isPartitioned', false).map((column) => { - return { - name: column.get('name'), - type: column.get('type.label'), - comment: column.get('comment'), - precision: column.get('precision'), - scale: column.get('scale') - } - }); - }, - - _getPartitionColumns(settings) { - return settings.columns.filterBy('isPartitioned', true).map((column) => { - return { - name: column.get('name'), - type: column.get('type.label'), - comment: column.get('comment'), - precision: column.get('precision'), - scale: column.get('scale') - } - }); - }, - - _getTableProperties(settings) { - let properties = {}; - settings.properties.forEach(function (property) { - properties[property.key] = property.value; - }); - - if (settings.settings.transactional) { - if (Ember.isEmpty(properties['transactional'])) { - properties['transactional'] = true; - } - } - - return properties; - } - - -}); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/services/table-operations.js ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/services/table-operations.js b/contrib/views/hive20/src/main/resources/ui/app/services/table-operations.js new file mode 100644 index 0000000..81b0430 --- /dev/null +++ b/contrib/views/hive20/src/main/resources/ui/app/services/table-operations.js @@ -0,0 +1,193 @@ +/** + * 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.Service.extend({ + store: Ember.inject.service(), + + submitCreateTable(database, settings) { + let detailedInfo = this._getDetailedInfo(settings); + let storageInfo = this._getStorageInfo(settings); + let columns = this._getColumns(settings); + let partitionColumns = this._getPartitionColumns(settings); + + let tableInfo = Ember.Object.create({ + database: database, + table: settings.name, + columns: columns, + partitionInfo: { columns: partitionColumns }, + detailedInfo: detailedInfo, + storageInfo: storageInfo + }); + return new Promise((resolve, reject) => { + this.get('store').adapterFor('table').createTable(tableInfo).then((data) => { + this.get('store').pushPayload(data); + resolve(this.get('store').peekRecord('job', data.job.id)); + }, (err) => { + reject(err); + }); + }); + }, + + deleteTable(database, table) { + return new Promise((resolve, reject) => { + this.get('store').adapterFor('table').deleteTable(database, table).then((data) => { + this.get('store').pushPayload(data); + resolve(this.get('store').peekRecord('job', data.job.id)); + }, (err) => { + reject(err); + }); + }) + }, + + waitForJobToComplete(jobId, after) { + return new Ember.RSVP.Promise((resolve, reject) => { + Ember.run.later(() => { + this.get('store').findRecord('job', jobId, {reload: true}) + .then((job) => { + let status = job.get('status').toLowerCase(); + if (status === 'succeeded') { + this._fetchDummyResult(jobId); + resolve(); + } else if (status === 'error') { + reject() + } else { + resolve(this.waitForJobToComplete(jobId, after)); + } + }, (error) => { + reject(error); + }); + }, after); + }); + }, + + _fetchDummyResult(jobId) { + this.get('store').adapterFor('job').fetchResult(jobId); + }, + + _getDetailedInfo(settings) { + let detailedInfo = {}; + detailedInfo['parameters'] = this._getTableProperties(settings); + + if (!Ember.isEmpty(settings.settings.location)) { + detailedInfo['location'] = settings.settings.location; + } + + return detailedInfo; + + }, + + _getStorageInfo(settings) { + const storageSettings = settings.settings; + let storageInfo = {}; + let parameters = {}; + + + + if (!(Ember.isEmpty(storageSettings.fileFormat) || Ember.isEmpty(storageSettings.fileFormat.type))) { + storageInfo.fileFormat = storageSettings.fileFormat.type; + if (storageSettings.fileFormat.type === 'CUSTOM Serde') { + storageInfo.inputFormat = storageSettings.inputFormat; + storageInfo.outputFormat = storageSettings.outputFormat; + } + } + + if (!Ember.isEmpty(storageSettings.rowFormat)) { + let addParameters = false; + if (!Ember.isEmpty(storageSettings.rowFormat.fieldTerminatedBy)) { + parameters['field.delim'] = String.fromCharCode(storageSettings.rowFormat.fieldTerminatedBy.id); + addParameters = true; + } + + if (!Ember.isEmpty(storageSettings.rowFormat.linesTerminatedBy)) { + parameters['line.delim'] = String.fromCharCode(storageSettings.rowFormat.linesTerminatedBy.id); + addParameters = true; + } + + if (!Ember.isEmpty(storageSettings.rowFormat.nullDefinedAs)) { + parameters['serialization.null.format'] = String.fromCharCode(storageSettings.rowFormat.fieldTerminatedBy.id); + addParameters = true; + } + + if (!Ember.isEmpty(storageSettings.rowFormat.escapeDefinedAs)) { + parameters['escape.delim'] = String.fromCharCode(storageSettings.rowFormat.linesTerminatedBy.id); + addParameters = true; + } + + if (addParameters) { + storageInfo.parameters = parameters; + } + } + + if (!Ember.isEmpty(settings.settings.numBuckets)) { + storageInfo['numBuckets'] = settings.settings.numBuckets; + } + + let clusteredColumnNames = settings.columns.filterBy('isClustered', true).map((column) => { + return column.get('name'); + }); + + if (clusteredColumnNames.length > 0) { + storageInfo['bucketCols'] = clusteredColumnNames; + } + + return storageInfo; + }, + + _getColumns(settings) { + return settings.columns.filterBy('isPartitioned', false).map((column) => { + return { + name: column.get('name'), + type: column.get('type.label'), + comment: column.get('comment'), + precision: column.get('precision'), + scale: column.get('scale') + } + }); + }, + + _getPartitionColumns(settings) { + return settings.columns.filterBy('isPartitioned', true).map((column) => { + return { + name: column.get('name'), + type: column.get('type.label'), + comment: column.get('comment'), + precision: column.get('precision'), + scale: column.get('scale') + } + }); + }, + + _getTableProperties(settings) { + let properties = {}; + settings.properties.forEach(function (property) { + properties[property.key] = property.value; + }); + + if (settings.settings.transactional) { + if (Ember.isEmpty(properties['transactional'])) { + properties['transactional'] = true; + } + } + + return properties; + } + + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/templates/components/jobs-browser.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/components/jobs-browser.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/components/jobs-browser.hbs index 84f16a7..47b499a 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/templates/components/jobs-browser.hbs +++ b/contrib/views/hive20/src/main/resources/ui/app/templates/components/jobs-browser.hbs @@ -45,7 +45,7 @@ Job Id - Title Dipayan + Title status Start time Duration http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table.hbs index 258f687..2d1d075 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table.hbs +++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table.hbs @@ -25,12 +25,26 @@ {{fa-icon "navicon"}}

+ {{#if showDeleteTableModal}} + {{#modal-dialog + translucentOverlay=true + container-class="modal-dialog modal-sm"}} + + {{/modal-dialog}} + {{/if}}
{{#tabs-pane tabs=tabs inverse= true as |tab|}} {{tabs-item tab=tab tabs=tabs}} @@ -39,4 +53,6 @@
+ + http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/details.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/details.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/details.hbs index 1577fec..225b6eb 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/details.hbs +++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/details.hbs @@ -56,7 +56,7 @@ Parameters -
{{toJson info.parameters}}
+
{{to-json info.parameters}}
{{/with}} http://git-wip-us.apache.org/repos/asf/ambari/blob/1e2fe429/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/storage.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/storage.hbs b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/storage.hbs index 6e76741..52209ac 100644 --- a/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/storage.hbs +++ b/contrib/views/hive20/src/main/resources/ui/app/templates/databases/database/tables/table/storage.hbs @@ -56,7 +56,7 @@ Parameters -
{{toJson info.parameters}}
+
{{to-json info.parameters}}
{{/with}}