You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/04/17 21:10:30 UTC
[1/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain,
Error handling and bugfixes (alexantonenko)
Repository: ambari
Updated Branches:
refs/heads/trunk 2917d0ded -> 672eee34d
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/view.xml b/contrib/views/hive/src/main/resources/view.xml
index ac97d58..9c8b103 100644
--- a/contrib/views/hive/src/main/resources/view.xml
+++ b/contrib/views/hive/src/main/resources/view.xml
@@ -17,10 +17,12 @@
<view>
<name>HIVE</name>
<label>Hive</label>
- <version>0.1.0</version>
+ <version>0.2.0</version>
<min-ambari-version>1.7.*</min-ambari-version>
+ <validator-class>org.apache.ambari.view.hive.PropertyValidator</validator-class>
+
<!-- HDFS Configs -->
<parameter>
<name>webhdfs.url</name>
@@ -159,10 +161,6 @@
<persistence>
<entity>
- <class>org.apache.ambari.view.hive.persistence.DataStoreStorage$SmokeTestEntity</class>
- <id-property>id</id-property>
- </entity>
- <entity>
<class>org.apache.ambari.view.hive.resources.jobs.viewJobs.JobImpl</class>
<id-property>id</id-property>
</entity>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
index d2ff02c..c5cc52b 100644
--- a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
@@ -70,6 +70,26 @@ public class ATSParserTest {
protected static class ATSRequestsDelegateStub implements ATSRequestsDelegate {
@Override
+ public String hiveQueryIdDirectUrl(String entity) {
+ return null;
+ }
+
+ @Override
+ public String hiveQueryIdOperationIdUrl(String operationId) {
+ return null;
+ }
+
+ @Override
+ public String tezDagDirectUrl(String entity) {
+ return null;
+ }
+
+ @Override
+ public String tezDagNameUrl(String name) {
+ return null;
+ }
+
+ @Override
public JSONObject hiveQueryIdList(String username) {
return (JSONObject) JSONValue.parse(
"{ \"entities\" : [ { \"domain\" : \"DEFAULT\",\n" +
@@ -409,4 +429,4 @@ public class ATSParserTest {
);
}
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
index 50642ac..10d052a 100644
--- a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
@@ -393,4 +393,4 @@ public class AggregatorTest {
this.hiveQueryIds = hiveQueryIds;
}
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c703bcf..eb9b587 100644
--- a/pom.xml
+++ b/pom.xml
@@ -300,6 +300,7 @@
<exclude>contrib/ambari-scom/metrics-sink/target/**</exclude>
<exclude>contrib/views/*/target/**</exclude>
<exclude>contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/**</exclude>
+ <exclude>contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc</exclude>
<exclude>contrib/views/files/src/main/resources/ui/bower_components/**</exclude>
<exclude>contrib/views/files/src/main/resources/ui/node/**</exclude>
<exclude>contrib/views/files/src/main/resources/ui/node_modules/**</exclude>
[3/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain,
Error handling and bugfixes (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
index 7106f0a..0901b30 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
@@ -18,11 +18,11 @@
<div {{bind-attr class=":alert :alert-dismissible message.typeClass"}}>
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" {{action "remove"}}>×</button>
- <strong>{{tb-helper message.title}}</strong>
+ <strong {{action 'toggleMessage'}}>{{tb-helper message.title}}</strong>
{{#if message.isExpanded}}
<div class="alert-message">
{{message.content}}
</div>
{{/if}}
-</div>
\ No newline at end of file
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
index f6af59b..230ec3b 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
@@ -18,7 +18,12 @@
<div>
<a {{action "toggle"}} {{bind-attr class=":fa iconClass"}}> {{heading}}</a>
+ <div class="pull-right">
+ {{#each control in controls}}
+ <a {{action 'sendControlAction' control.action}} {{bind-attr class=":fa control.icon" title="control.tooltip"}}></a>
+ {{/each}}
+ </div>
</div>
{{#if isExpanded}}
{{yield}}
-{{/if}}
\ No newline at end of file
+{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
new file mode 100644
index 0000000..5f905a8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
@@ -0,0 +1,21 @@
+{{!
+* 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.
+}}
+
+{{#each notification in notifications}}
+ {{view "notification" notification=notification}}
+{{/each}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
index 45c4edf..1e29033 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
@@ -16,7 +16,7 @@
* limitations under the License.
}}
-<div class="panel panel-default" {{bind-attr class=classNames}}>
+<div {{bind-attr class=":panel :panel-default classNames"}}>
{{#if heading}}
<div class="panel-heading">
{{#if menuItems}}
@@ -34,6 +34,14 @@
</ul>
</div>
{{/if}}
+
+ {{#if iconActions}}
+ {{#each iconAction in iconActions}}
+ <i {{action "sendMenuItemAction" iconAction.action}}
+ {{bind-attr class=":pull-right :panel-action-icon :fa iconAction.icon"}}></i>
+ {{/each}}
+ {{/if}}
+
<strong>{{heading}}</strong>
{{#if isLoading}}
<div class="spinner small pull-right"></div>
@@ -43,4 +51,4 @@
<div class="panel-body">
{{yield}}
</div>
-</div>
\ No newline at end of file
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
new file mode 100644
index 0000000..f131367
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
@@ -0,0 +1,29 @@
+{{!
+* 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.
+}}
+
+{{#each tab in tabs}}
+ <span {{action tab.action tab}} {{bind-attr class=":query-menu-tab tabClassNames tab.iconClass tab.active:active"}}>
+ {{#if tab.badge}}
+ <span class="badge">{{tab.badge}}</span>
+ {{/if}}
+
+ {{#if tab.text}}
+ {{tab.text}}
+ {{/if}}
+ </span>
+{{/each}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
index e5f313a..5f9c18c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
@@ -23,6 +23,7 @@
{{#link-to tab.path tab.id tagName="li"}}
<a>
{{tab.name}}
+ {{#if tab.isDirty}}*{{/if}}
{{#if view.removeEnabled}}
<i class="fa fa-remove" {{action 'remove' tab}}></i>
{{/if}}
@@ -37,4 +38,4 @@
{{/each}}
</ul>
-{{yield}}
\ No newline at end of file
+{{yield}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
index 354d7bd..007d9ca 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
@@ -44,5 +44,7 @@
</div>
</div>
{{else}}
- <h4>{{t "labels.noTablesMatches"}} "{{tablesSearchTerm}}"</h4>
+ <div class="alert alert-warning database-explorer-alert" role="alert">
+ {{t "labels.noTablesMatch"}} <strong>"{{tablesSearchTerm}}"</strong>
+ </div>
{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
index 5da9760..bdac484 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
@@ -18,11 +18,11 @@
<div class="databases">
{{#each database in content}}
- {{#collapsible-widget heading=database.name isExpanded=database.isExpanded iconClass="fa-database" expanded="getTables"}}
+ {{#collapsible-widget heading=database.name isExpanded=database.isExpanded iconClass="fa-database" expanded="getTables" toggledParam=database}}
{{#if database.isExpanded}}
<div class="tables">
{{#each table in database.visibleTables}}
- {{#collapsible-widget heading=table.name isExpanded=table.isExpanded toggledParam=database iconClass="fa-th" expanded="getColumns"}}
+ {{#collapsible-widget heading=table.name isExpanded=table.isExpanded toggledParam=database iconClass="fa-table" expanded="getColumns" controls=tableControls}}
{{#if table.isExpanded}}
<div class="columns">
{{#each column in table.visibleColumns}}
@@ -45,4 +45,4 @@
{{/if}}
{{/collapsible-widget}}
{{/each}}
-</div>
\ No newline at end of file
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
index a0ce19f..392b8f3 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
@@ -16,7 +16,7 @@
* limitations under the License.
}}
-{{#panel-widget headingTranslation="titles.database" isLoading=isLoading classNames="database-explorer"}}
+{{#panel-widget headingTranslation="titles.database" isLoading=isLoading classNames="database-explorer" iconActions=panelIconActions}}
{{#if model}}
{{typeahead-widget
@@ -51,4 +51,4 @@
{{partial selectedTab.view}}
{{/tabs-widget}}
{{/if}}
-{{/panel-widget}}
\ No newline at end of file
+{{/panel-widget}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
index 8621baa..1551072 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
@@ -23,22 +23,22 @@
</aside>
<div class="col-md-9 col-xs-12 query-container">
- {{render 'alerts'}}
{{#panel-widget headingTranslation="titles.query.editor" classNames="query-editor-panel"}}
{{render 'open-queries'}}
<div class="toolbox">
- <button type="button" class="btn btn-sm btn-success execute-query"
- {{bind-attr class="canExecute::disabled"}}
+ <button type="button"
+ {{bind-attr class=":btn :btn-sm :btn-success :execute-query canExecute::disabled"}}
{{action "executeQuery"}}>
{{t "buttons.execute"}}
</button>
- <button type="button" class="btn btn-sm btn-default"
- {{bind-attr class="canExecute::disabled"}}
+ <button type="button"
+ {{bind-attr class=":btn :btn-sm :btn-default canExecute::disabled"}}
{{action "explainQuery"}}>
{{t "buttons.explain"}}
</button>
+
<button type="button" class="btn btn-sm btn-default save-query-as" {{action "saveQuery"}}>{{t "buttons.saveAs"}}</button>
{{render 'insert-udfs'}}
@@ -76,13 +76,7 @@
</div>
</div>
- {{#if tezUI.showOverlay}}
- {{render 'tez-ui'}}
- {{/if}}
-
- {{#if visualExplain.showOverlay}}
- {{render 'visual-explain'}}
- {{/if}}
+ {{outlet 'overlay'}}
<div class="query-menu">
{{#popover-widget classNames="fa fa-info-circle queries-icon" titleTranslation="popover.queryEditorHelp.title" }}
@@ -93,10 +87,6 @@
</ul>
{{/popover-widget}}
- <span {{bind-attr class="settings.showOverlay:active :fa :fa-gear :queries-icon"}} {{action 'toggleOverlay' 'settings'}}></span>
-
- <span {{bind-attr class="visualExplain.showOverlay:active :fa :fa-bar-chart :queries-icon"}} {{action 'toggleOverlay' 'visualExplain'}}></span>
-
- <span {{bind-attr class="tezUI.showOverlay:active shouldShowTez::hide :queries-icon :text-icon"}} {{action 'toggleOverlay' 'tezUI'}}>TEZ</span>
+ {{query-tabs}}
</div>
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
index ed6fba6..06c21e4 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
@@ -20,13 +20,13 @@
<table class="table table-expandable">
<thead>
<tr>
- {{#each column in results.schema}}
+ {{#each column in formattedResults.schema}}
<th> {{column.name}} </th>
{{/each}}
</tr>
</thead>
<tbody>
- {{#each row in results.rows}}
+ {{#each row in formattedResults.rows}}
<tr>
{{#each item in row}}
<td>{{item}}</td>
@@ -44,4 +44,4 @@
</div>
{{else}}
{{error}}
-{{/if}}
\ No newline at end of file
+{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
index 0ea2c99..0911835 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
@@ -16,29 +16,31 @@
* limitations under the License.
}}
-<div class="dropdown">
- <a role="button" data-toggle="dropdown" class="btn btn-default btn-sm" data-target="#">
- {{t "placeholders.select.udfs"}}
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu">
- {{#each item in this}}
- <li class="dropdown dropdown-submenu">
- {{#if item.file}}
- <a tabindex="-1">{{item.file.name}}</a>
- {{else}}
- <a tabindex="-1">{{tb-helper item.name}}</a>
- {{/if}}
- <ul class="dropdown-menu">
- {{#each udf in item.udfs}}
- <li>
- {{#no-bubbling click="insertUdf" data=udf tagName="a"}}
- {{udf.name}}
- {{/no-bubbling}}
- </li>
- {{/each}}
- </ul>
- </li>
- {{/each}}
- </ul>
-</div>
\ No newline at end of file
+{{#if this.length}}
+ <div class="dropdown">
+ <a role="button" data-toggle="dropdown" class="btn btn-default btn-sm" data-target="#">
+ {{t "placeholders.select.udfs"}}
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu">
+ {{#each item in this}}
+ <li class="dropdown dropdown-submenu">
+ {{#if item.file}}
+ <a tabindex="-1">{{item.file.name}}</a>
+ {{else}}
+ <a tabindex="-1">{{tb-helper item.name}}</a>
+ {{/if}}
+ <ul class="dropdown-menu">
+ {{#each udf in item.udfs}}
+ <li>
+ {{#no-bubbling click="insertUdf" data=udf tagName="a"}}
+ {{udf.name}}
+ {{/no-bubbling}}
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+{{/if}}
\ 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/message.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs
new file mode 100644
index 0000000..ad7fb9a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs
@@ -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.
+}}
+
+<div {{bind-attr class=":alert :notification view.typeClass"}}>
+ <button type="button" class="close" {{action "close" target="view"}}><span aria-hidden="true">×</span></button>
+ <i {{bind-attr class=":fa view.typeIcon"}}></i>
+
+ {{#if view.notification.body}}
+ <a {{action "expand" target="view"}}>
+ {{view.notification.message}}
+ </a>
+ {{else}}
+ {{view.notification.message}}
+ {{/if}}
+
+ {{#if view.isExpanded}}
+ <pre class="message-body">
+ {{preformatted-string view.notification.body}}
+ </pre>
+ {{/if}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
new file mode 100644
index 0000000..7c494b6
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
@@ -0,0 +1,30 @@
+{{!
+* 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 class="editor-overlay messages-container">
+ <h3>Messages
+ {{#if messages.length}}
+ <button class="btn btn-danger btn-xs" {{action 'removeAllMessages'}}><i class="fa fa-minus"></i> Clear All</button>
+ {{/if}}
+ </h3>
+
+
+ {{#each message in messages}}
+ {{view 'message' notification=message}}
+ {{/each}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
index a870a0e..032c144 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
@@ -17,5 +17,5 @@
}}
{{#modal-widget heading=heading close="close" ok="delete"}}
- {{text}}
+ {{tb-helper text}}
{{/modal-widget}}
\ 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/modal-save-query.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs
new file mode 100644
index 0000000..6853550
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs
@@ -0,0 +1,24 @@
+{{!
+* 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.
+}}
+
+{{#modal-widget heading=heading close="close" ok="save"}}
+ {{input type="text" class="form-control" value=text }}
+ {{#if showMessage}}
+ <span class="label label-warning">{{tb-helper message}}</span>
+ {{/if}}
+{{/modal-widget}}
\ 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/notification.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs
new file mode 100644
index 0000000..35c2fe1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs
@@ -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.
+}}
+
+<div {{bind-attr class=":alert :notification view.typeClass"}}>
+ <button type="button" class="close" {{action "close" target="view"}}><span aria-hidden="true">×</span></button>
+ <i {{bind-attr class=":fa view.typeIcon"}}></i>
+ {{view.notification.message}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
index ad1d9bd..fdf5597 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
@@ -17,7 +17,7 @@
}}
{{#tabs-widget tabs=queryTabs removeClicked="removeQueryTab" canRemove=true}}
- {{render 'settings'}}
+ {{outlet 'overlay'}}
{{query-editor tables=selectedTables query=currentQuery.fileContent editor=view.editor highlightedText=highlightedText
columnsNeeded="getColumnsForAutocomplete"}}
{{/tabs-widget}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
index 791b7d0..98f0b54 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
@@ -16,44 +16,51 @@
* limitations under the License.
}}
-{{#if showOverlay}}
- <div class="settings-container fadeIn">
- <h3> Settings
- <button class="btn btn-success btn-xs" {{action 'add'}}><i class="fa fa-plus"></i> Add</button>
- </h3>
+<div class="editor-overlay settings-container fadeIn">
+ <h3> Settings
+ <button class="btn btn-success btn-xs" {{action 'add'}}><i class="fa fa-plus"></i> Add</button>
+ {{#if currentSettings.settings}}
+ <button class="btn btn-danger btn-xs" {{action 'removeAll'}}><i class="fa fa-minus"></i> Remove All</button>
+ {{/if}}
- {{#each setting in currentSettings.settings}}
- <div class="setting col-md-6 col-sm-12">
- <form>
- <div class="form-group">
- <div class="input-group">
- <div class="input-group-addon">
- {{typeahead-widget
- content=predefinedSettings
- optionLabelPath="name"
- optionValuePath="name"
- selection=setting.key
- create="addKey"
- }}
- </div>
- <div {{bind-attr class=":input-group-addon setting.valid::has-error"}}>
- {{#if setting.key.values}}
- {{select-widget items=setting.key.values
- labelPath="value"
- selectedValue=setting.selection
- defaultLabelTranslation="placeholders.select.value"
- }}
- {{else}}
- {{input class="input-sm form-control" placeholderTranslation="placeholders.select.value" value=setting.selection.value}}
- {{/if}}
+ {{#if canInvalidateSession}}
+ <button class="btn btn-danger btn-xs pull-right" {{action 'invalidateSession'}}><i class="fa fa-times"></i> Invalidate Session</button>
+ {{/if}}
+ </h3>
+
+ {{#each setting in currentSettings.settings}}
+ <div class="setting col-md-6 col-sm-12">
+ <form>
+ <div class="form-group">
+ <div class="input-group">
+ <div class="input-group-addon">
+ {{typeahead-widget
+ options=predefinedSettings
+ excluded=selectedSettings
+ optionLabelPath="name"
+ optionValuePath="name"
+ selection=setting.key
+ create="addKey"
+ }}
+ </div>
+ <div {{bind-attr class=":input-group-addon setting.valid::has-error"}}>
+
+ {{#if setting.key.values}}
+ {{select-widget items=setting.key.values
+ labelPath="value"
+ selectedValue=setting.selection
+ defaultLabelTranslation="placeholders.select.value"
+ }}
+ {{else}}
+ {{input class="input-sm form-control" placeholderTranslation="placeholders.select.value" value=setting.selection.value}}
+ {{/if}}
- <span class="fa fa-times-circle remove pull-right" {{action 'remove' setting}}></span>
- </div>
+ <span class="fa fa-times-circle remove pull-right" {{action 'remove' setting}}></span>
</div>
</div>
- </form>
- </div>
- {{/each}}
- </div>
-{{/if}}
+ </div>
+ </form>
+ </div>
+ {{/each}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
index 4bee4d6..4bc0c0e 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
@@ -23,6 +23,8 @@
{{else}}
{{#if error}}
<div class="alert alert-danger" role="alert"><strong>{{tb-helper error}}</strong></div>
+ {{else}}
+ <div class="alert alert-danger" role="alert"><strong>{{tb-helper 'tez.errors.no.dag'}}</strong></div>
{{/if}}
{{/if}}
{{/panel-widget}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
index a823ef2..a831b8c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
@@ -18,5 +18,63 @@
<div id="visual-explain">
{{#panel-widget headingTranslation="titles.query.visualExplain"}}
+
+ {{#each edge in view.edges}}
+ <div class="edge">
+ <div class="edge-path" {{bind-attr style="edge.style"}}>
+ {{edge.type}}
+ </div>
+ {{!-- <div class="edge-arrow" ></div> --}}
+ </div>
+ {{/each}}
+
+ <div class="nodes">
+ {{#each group in view.verticesGroups}}
+ <div class="node-container">
+ {{#if group.contents}}
+ {{#each node in group.contents}}
+ <div {{bind-attr class="node.isTableNode:table-node node.isOutputNode:output-node :node" title="node.id"}}>
+ {{#if node.isTableNode}}
+ <p><strong>{{t 'labels.table'}}</strong></p>
+ {{node.label}}
+ {{else}}
+ {{#if node.isOutputNode}}
+ {{node.label}}
+ {{else}}
+ <div class="node-heading">
+ <strong>{{node.label}}</strong>
+ </div>
+ <div class="node-content">
+ {{#each section in node.contents}}
+ <p>
+ {{#popover-widget classNames="fa fa-info-circle" titleTranslation="popover.visualExplain.statistics" }}
+ {{section.statistics}}
+ {{/popover-widget}}
+ <strong>
+ {{section.index}}. {{section.title}}
+ </strong>
+ {{section.value}}
+ </p>
+
+ {{#each field in section.fields}}
+ {{#if field.value}}
+ <p>{{field.label}} {{field.value}}</p>
+ {{/if}}
+ {{/each}}
+ {{/each}}
+ </div>
+ {{/if}}
+ {{/if}}
+ </div>
+ {{/each}}
+ {{else}}
+ <div class="node" {{bind-attr title="group.label"}}>
+ {{group.label}}
+ </div>
+ {{/if}}
+ </div>
+ {{/each}}
+ </div>
+
{{/panel-widget}}
</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/utils/constants.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
index 888275d..b099ce6 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
@@ -55,6 +55,7 @@ export default Ember.Object.create({
udfInsertPrefix: 'create temporary function ',
fileInsertPrefix: 'add jar ',
explainPrefix: 'EXPLAIN ',
+ explainFormattedPrefix: 'EXPLAIN FORMATTED ',
insertUdfs: 'insert-udfs',
job: 'job',
jobs: 'jobs',
@@ -180,7 +181,7 @@ export default Ember.Object.create({
//this can be replaced by a string.format implementation
adapter: {
- version: '0.0.1',
+ version: '0.2.0',
instance: 'Hive',
apiPrefix: '/api/v1/views/HIVE/versions/',
instancePrefix: '/instances/',
@@ -189,5 +190,25 @@ export default Ember.Object.create({
settings: {
executionEngine: 'hive.execution.engine'
+ },
+ sampleDataQuery: 'SELECT * FROM %@ LIMIT 100;',
+
+ notify: {
+ ERROR: {
+ typeClass : 'alert-danger',
+ typeIcon : 'fa-exclamation-triangle'
+ },
+ WARN: {
+ typeClass : 'alert-warning',
+ typeIcon : 'fa-times-circle'
+ },
+ SUCCESS: {
+ typeClass : 'alert-success',
+ typeIcon : 'fa-check'
+ },
+ INFO: {
+ typeClass : 'alert-info',
+ typeIcon : 'fa-info'
+ }
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
new file mode 100644
index 0000000..f4b7f5d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
@@ -0,0 +1,141 @@
+/**
+ * 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.ArrayProxy.create({
+ content: Ember.A(
+ [
+ {
+ targetOperator: 'TableScan',
+ targetProperty: 'alias:',
+ label: 'Table Scan:',
+
+ fields: [
+ {
+ label: 'filterExpr:',
+ targetProperty: 'filterExpr:'
+ }
+ ]
+ },
+ {
+ targetOperator: 'Filter Operator',
+ targetProperty: 'predicate:',
+ label: 'Filter:',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Map Join Operator',
+ label: 'Map Join',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Merge Join Operator',
+ label: 'Merge Join',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Select Operator',
+ label: 'Select',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Reduce Output Operator',
+ label: 'Reduce',
+
+ fields: [
+ {
+ label: 'Partition columns:',
+ targetProperty: 'Map-reduce partition columns:'
+ },
+ {
+ label: 'Key expressions:',
+ targetProperty: 'key expressions:'
+ },
+ {
+ label: 'Sort order:',
+ targetProperty: 'sort order:'
+ }
+ ]
+ },
+ {
+ targetOperator: 'File Output Operator',
+ label: 'File Output Operator',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Group By Operator',
+ label: 'Group By:',
+
+ fields: [
+ {
+ label: 'Aggregations:',
+ targetProperties: 'aggregations:'
+ },
+ {
+ label: 'Keys:',
+ targetProperty: 'keys:'
+ }
+ ]
+ },
+ {
+ targetOperator: 'Limit',
+ targetProperty: 'Number of rows:',
+ label: 'Limit:',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Extract',
+ label: 'Extract',
+
+ fields: []
+ },
+ {
+ targetOperator: 'PTF Operator',
+ label: 'Partition Table Function',
+
+ fields: []
+ },
+ {
+ targetOperator: 'Dynamic Partitioning Event Operator',
+ labelel: 'Dynamic Partitioning Event',
+
+ fields: [
+ {
+ label: 'Target column:',
+ targetProperty: 'Target column:'
+ },
+ {
+ label: 'Target Vertex:',
+ targetProperty: 'Target Vertex:'
+ },
+ {
+ label: 'Partition key expr:',
+ targetProperty: 'Partition key expr:'
+ }
+ ]
+ }
+ ]
+ )
+});
\ 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/views/message.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js
new file mode 100644
index 0000000..dd32a15
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js
@@ -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.
+ */
+import Ember from 'ember';
+import NotificationView from 'hive/views/notification';
+
+export default NotificationView.extend({
+ templateName : 'message',
+ removeLater : Ember.K,
+ isExpanded : false,
+ removeMessage: 'removeMessage',
+
+ actions: {
+ expand: function() {
+ this.toggleProperty('isExpanded');
+ },
+
+ close: function() {
+ this.get('controller').send('removeMessage', this.get('notification'));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
new file mode 100644
index 0000000..291bfeb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
@@ -0,0 +1,51 @@
+/**
+ * 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.View.extend({
+ closeAfter : 500000,
+ isHovering : false,
+ templateName : 'notification',
+ removeNotification : 'removeNotification',
+
+ setup: function() {
+ this.set('typeClass', this.get('notification.type.typeClass'));
+ this.set('typeIcon', this.get('notification.type.typeIcon'));
+ }.on('init'),
+
+ removeLater: function() {
+ Ember.run.later(this, function() {
+ if (this.get('isHovering')) {
+ this.removeLater();
+ } else if (this.element) {
+ this.send('close');
+ }
+ }, this.get('closeAfter'));
+ }.on('didInsertElement'),
+
+ mouseEnter: function() { this.set('isHovering', true); },
+ mouseLeave: function() { this.set('isHovering', false); },
+
+ actions: {
+ close: function() {
+ this.remove();
+ this.get('parentView').send('removeNotification', this.get('notification'));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
index 4a887e2..07baedb 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
@@ -17,13 +17,28 @@
*/
import Ember from 'ember';
+import dagRules from '../utils/dag-rules';
export default Ember.View.extend({
+ willInsertElement: function () {
+ this.set('verticesGroups', []);
+ this.set('edges', []);
+ this.set('graph', new dagre.graphlib.Graph());
+
+ if (this.get('controller.json')) {
+ this.renderDag();
+ }
+ },
+
didInsertElement: function () {
+ this._super();
+
var target = this.$('#visual-explain');
target.css('min-height', $('.main-content').height());
target.animate({ width: $('.main-content').width() }, 'fast');
+
+ Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
},
willDestroyElement: function () {
@@ -31,5 +46,391 @@ export default Ember.View.extend({
target.css('min-height', 0);
target.css('width', 0);
+ },
+
+ getOffset: function (el) {
+ var _x = 0;
+ var _y = 0;
+ var _w = el.offsetWidth|0;
+ var _h = el.offsetHeight|0;
+ while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
+ _x += el.offsetLeft - el.scrollLeft;
+ _y += el.offsetTop - el.scrollTop;
+ el = el.offsetParent;
+ }
+ return { top: _y, left: _x, width: _w, height: _h };
+ },
+
+ addEdge: function (div1, div2, thickness, type) {
+ var off1 = this.getOffset(div1);
+ var off2 = this.getOffset(div2);
+ // bottom right
+ var x1 = off1.left + off1.width / 2;
+ var y1 = off1.top + off1.height;
+ // top right
+ var x2 = off2.left + off2.width / 2;
+ var y2 = off2.top;
+ // distance
+ var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)));
+ // center
+ var cx = ((x1 + x2) / 2) - (length / 2);
+ var cy = ((y1 + y2) / 2) - (thickness / 2) - 73;
+ // angle
+ var angle = Math.round(Math.atan2((y1-y2), (x1-x2)) * (180 / Math.PI));
+
+ if (angle < -90) {
+ angle = 180 + angle;
+ }
+
+ var style = "left: %@px; top: %@px; width: %@px;" +
+ "-moz-transform:rotate(%@4deg);" +
+ "-webkit-transform:rotate(%@4deg);" +
+ "-ms-transform:rotate(%@4deg);" +
+ "-transform:rotate(%@4deg);";
+
+ style = style.fmt(cx, cy, length, angle);
+
+ var edgeType;
+
+ if (type) {
+ if (type === 'BROADCAST_EDGE') {
+ edgeType = 'BROADCAST';
+ } else {
+ edgeType = 'SHUFFLE';
+ }
+ }
+
+ this.get('edges').pushObject({
+ style: style,
+ type: edgeType
+ });
+ },
+
+ afterRenderEvent : function () {
+ var g = this.get('graph');
+ var self = this;
+
+ //draw edges after the slide aniamtion for the visual explain container is done
+ Ember.run.later(function () {
+ g.edges().forEach(function (value) {
+ var edge = g.edge(value);
+ var v = value.v;
+
+ var firstNode = self.$("[title='" + value.v + "']")[0];
+ var secondNode = self.$("[title='" + value.w + "']")[0];
+
+ self.addEdge(firstNode, secondNode, 2, g.edge(value).type);
+ });
+ }, 300);
+ },
+
+ getNodeContents: function (operator, contents, table, vertex) {
+ var currentTable = table,
+ contents = contents || [],
+ nodeName,
+ node,
+ ruleNode,
+ nodeLabelValue,
+ self = this;
+
+ if (operator.constructor === Array) {
+ operator.forEach(function (childOperator) {
+ self.getNodeContents(childOperator, contents, currentTable, vertex);
+ });
+
+ return contents;
+ } else {
+ nodeName = Object.getOwnPropertyNames(operator)[0];
+ node = operator[nodeName];
+ ruleNode = dagRules.findBy('targetOperator', nodeName);
+
+ if (ruleNode) {
+ if (nodeName.indexOf('Map Join') > -1) {
+ nodeLabelValue = this.handleMapJoinNode(node, currentTable);
+ currentTable = null;
+ } else if (nodeName.indexOf('Merge Join') > -1) {
+ nodeLabelValue = this.handleMergeJoinNode(node, vertex);
+ } else {
+ nodeLabelValue = node[ruleNode.targetProperty];
+ }
+
+ contents.pushObject({
+ title: ruleNode.label,
+ statistics: node["Statistics:"],
+ index: contents.length + 1,
+ value: nodeLabelValue,
+ fields: ruleNode.fields.map(function (field) {
+ var value = node[field.targetProperty || field.targetProperties];
+
+ return {
+ label: field.label,
+ value: value
+ }
+ })
+ });
+
+ if (node.children) {
+ return this.getNodeContents(node.children, contents, currentTable, vertex);
+ } else {
+ return contents;
+ }
+ } else {
+ return contents;
+ }
+ }
+ },
+
+ handleMapJoinNode: function (node, table) {
+ var rows = table || "<rows from above>";
+ var firstTable = node["input vertices:"][0] || rows;
+ var secondTable = node["input vertices:"][1] || rows;
+
+ var joinString = node["condition map:"][0][""];
+ joinString = joinString.replace("0", firstTable);
+ joinString = joinString.replace("1", secondTable);
+ joinString += " on ";
+ joinString += node["keys:"][0] + "=";
+ joinString += node["keys:"][1];
+
+ return joinString;
+ },
+
+ handleMergeJoinNode: function (node, vertex) {
+ var graphData = this.get('controller.json')['STAGE PLANS']['Stage-1']['Tez'];
+ var edges = graphData['Edges:'];
+ var index = 0;
+ var joinString = node["condition map:"][0][""];
+
+ edges[vertex].toArray().forEach(function (edge) {
+ if (edge.type === "SIMPLE_EDGE") {
+ joinString.replace(String(index), edge.parent);
+ index++;
+ }
+ });
+
+ return joinString;
+ },
+
+ //sets operator nodes
+ setNodes: function (vertices) {
+ var g = this.get('graph');
+ var self = this;
+
+ vertices.forEach(function (vertex) {
+ var contents = [];
+ var operator;
+ var currentTable;
+
+ if (vertex.name.indexOf('Map') > -1) {
+ operator = vertex.value['Map Operator Tree:'][0];
+ currentTable = operator["TableScan"]["alias:"];
+ } else if (vertex.name.indexOf('Reducer') > -1) {
+ operator = vertex.value['Reduce Operator Tree:'];
+ }
+
+ // else if (vertex.name.indexOf('Union') > -1) {
+ // g.setNode(vertex, {
+ // id: vertex.name,
+ // label: vertex.name
+ // });
+ // }
+
+ if (operator) {
+ contents = self.getNodeContents(operator, null, currentTable, vertex.name);
+
+ g.setNode(vertex.name, {
+ contents: contents,
+ id: vertex.name,
+ label: vertex.name
+ });
+ }
+ });
+
+ return this;
+ },
+
+ //sets edges between operator nodes
+ setEdges: function (edges) {
+ var i;
+ var g = this.get('graph');
+ var invalidEdges = [];
+ var edgesToBeRemoved = [];
+ var isValidEdgeType = function (type) {
+ return type === "SIMPLE_EDGE" ||
+ type === "BROADCAST_EDGE";
+ };
+
+ edges.forEach(function (edge) {
+ var parent;
+ var type;
+
+ if (edge.value.constructor === Array) {
+ edge.value.forEach(function (childEdge) {
+ parent = childEdge.parent;
+ type = childEdge.type;
+
+ if (isValidEdgeType(type)) {
+ g.setEdge(parent, edge.name);
+ g.edge({v: parent, w: edge.name}).type = type;
+ } else {
+ invalidEdges.pushObject({
+ vertex: edge.name,
+ edge: childEdge
+ });
+ }
+ });
+ } else {
+ parent = edge.value.parent;
+ type = edge.value.type;
+
+ if (isValidEdgeType(type)) {
+ g.setEdge(parent, edge.name);
+ g.edge({v: parent, w: edge.name}).type = type;
+ } else {
+ invalidEdges.pushObject({
+ vertex: edge.name,
+ edge: edge.name
+ });
+ }
+ }
+ });
+
+ invalidEdges.forEach(function (invalidEdge) {
+ var targetEdge = g.edges().find(function (graphEdge) {
+ return graphEdge.v === invalidEdge.edge.parent ||
+ graphEdge.w === invalidEdge.edge.parent;
+ });
+
+ var targetVertex;
+
+ if (targetEdge) {
+ edgesToBeRemoved.pushObject(targetEdge);
+
+ if (targetEdge.v === invalidEdge.edge.parent) {
+ targetVertex = targetEdge.w;
+ } else {
+ targetVertex = targetEdge.v;
+ }
+
+ parent = invalidEdge.vertex;
+
+ g.setEdge({v: parent, w: targetVertex});
+ g.setEdge({v: parent, w: targetVertex}).type = "BROADCAST_EDGE";
+ }
+ });
+
+ edgesToBeRemoved.uniq().forEach(function (edge) {
+ g.removeEdge(edge.v, edge.w, edge.name);
+ });
+
+ return this;
+ },
+
+ //sets nodes for tables and their edges
+ setTableNodesAndEdges: function (vertices) {
+ var g = this.get('graph');
+
+ vertices.forEach(function (vertex) {
+ var operator;
+ var table;
+ var id;
+
+ if (vertex.name.indexOf('Map') > -1) {
+ operator = vertex.value['Map Operator Tree:'][0];
+ for (var node in operator) {
+ table = operator[node]['alias:'];
+
+ //create unique identifier by using table + map pairs so that we have
+ //different nodes for the same table if it's a table connected to multiple Map operators
+ id = table + ' for ' + vertex.name;
+
+ g.setNode(id, { id: id, label: table, isTableNode: true });
+ g.setEdge(id, vertex.name);
+ }
+ }
+ });
+
+ return this;
+ },
+
+ createNodeGroups: function () {
+ var groupedNodes = [];
+ var g = this.get('graph');
+ var lastRowNode;
+ var fileOutputOperator;
+
+ g.nodes().forEach(function (value) {
+ var node = g.node(value);
+
+ if (node) {
+ var existentRow = groupedNodes.findBy('topOffset', node.y);
+
+ if (!existentRow) {
+ groupedNodes.pushObject({
+ topOffset: node.y,
+ contents: [ node ]
+ });
+ } else {
+ existentRow.contents.pushObject(node);
+ }
+ }
+ });
+
+ groupedNodes = groupedNodes.sortBy('topOffset');
+ groupedNodes.forEach(function (group) {
+ group.contents = group.contents.sortBy('x');
+ });
+
+ lastRowNode = groupedNodes.get('lastObject.contents.lastObject');
+ fileOutputOperator = lastRowNode.contents.get('lastObject');
+
+ g.setNode(fileOutputOperator.title, { id: fileOutputOperator.title, label: fileOutputOperator.title, isOutputNode: true });
+ g.setEdge(fileOutputOperator.title, lastRowNode.id);
+
+ groupedNodes.pushObject({
+ contents: [ g.node(fileOutputOperator.title) ]
+ });
+
+ lastRowNode.contents.removeObject(fileOutputOperator);
+
+ this.set('verticesGroups', groupedNodes);
+ },
+
+ renderDag: function () {
+ var convert = function (inputObj) {
+ var array = [];
+
+ for (var key in inputObj) {
+ if (inputObj.hasOwnProperty(key)) {
+ array.pushObject({
+ name: key,
+ value: inputObj[key]
+ });
+ }
+ }
+
+ return array;
+ };
+
+ // Create a new directed graph
+ var g = this.get('graph');
+
+ var graphData = this.get('controller.json')['STAGE PLANS']['Stage-1']['Tez'];
+ var vertices = convert(graphData['Vertices:']);
+ var edges = convert(graphData['Edges:']);
+
+ // Set an object for the graph label
+ g.setGraph({});
+
+ // Default to assigning a new object as a label for each new edge.
+ g.setDefaultEdgeLabel(function () { return {}; });
+
+ this.setNodes(vertices)
+ .setEdges(edges)
+ .setTableNodesAndEdges(vertices);
+
+ dagre.layout(g);
+
+ this.createNodeGroups();
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/bower.json b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
index 161a626..24e8821 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
@@ -1,27 +1,25 @@
{
"name": "hive",
"dependencies": {
- "handlebars": "2.0.0",
"jquery": "^1.11.1",
- "ember": "1.9.0",
- "ember-data": "1.0.0-beta.14.1",
- "ember-resolver": "~0.1.7",
- "loader.js": "stefanpenner/loader.js#1.0.1",
+ "ember": "1.10.0",
+ "ember-data": "1.0.0-beta.16.1",
+ "ember-resolver": "~0.1.12",
+ "loader.js": "stefanpenner/loader.js#3.2.0",
"ember-cli-shims": "stefanpenner/ember-cli-shims#0.0.3",
- "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.0.4",
+ "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.1.3",
"ember-load-initializers": "stefanpenner/ember-load-initializers#0.0.2",
"ember-qunit": "0.2.8",
"ember-qunit-notifications": "0.0.7",
- "qunit": "~1.15.0",
+ "qunit": "~1.17.1",
"bootstrap": "~3.2.0",
- "ember-i18n": "~2.9.0",
+ "ember-i18n": "~3.0.0",
"blanket": "~1.1.5",
"jquery-ui": "~1.11.2",
"selectize": "~0.12.0",
"pretender": "0.1.0"
},
"resolutions": {
- "handlebars": "2.0.0",
- "ember": "1.9.0"
+ "ember": "1.10.0"
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/package.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/package.json b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
index c2523b1..95f6897 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/package.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
@@ -22,24 +22,24 @@
"devDependencies": {
"body-parser": "^1.2.0",
"bower": ">= 1.3.12",
- "broccoli-asset-rev": "0.3.1",
- "broccoli-sass": "^0.3.2",
- "ember-cli": "0.1.15",
- "ember-cli-blanket": "^0.2.2",
+ "broccoli-asset-rev": "^2.0.0",
+ "broccoli-sass": "0.6.3",
+ "ember-cli": "0.2.2",
+ "ember-cli-blanket": "^0.4.0",
"ember-cli-content-security-policy": "0.3.0",
"ember-cli-font-awesome": "0.0.4",
- "ember-cli-htmlbars": "^0.5.3",
+ "ember-cli-htmlbars": "0.7.4",
"ember-cli-ic-ajax": "0.1.1",
"ember-cli-inject-live-reload": "^1.3.0",
"ember-cli-jquery-ui": "0.0.12",
"ember-cli-moment": "0.0.1",
"ember-cli-pretender": "^0.3.1",
- "ember-cli-qunit": "0.3.7",
+ "ember-cli-qunit": "0.3.9",
"ember-cli-selectize": "0.0.19",
- "ember-data": "1.0.0-beta.14.1",
+ "ember-data": "1.0.0-beta.16.1",
"ember-dynamic-component": "0.0.1",
"ember-export-application-global": "^1.0.0",
"express": "^4.8.5",
- "glob": "4.4.0"
+ "ember-cli-uglify": "1.0.1"
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/testem.json b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
index eff93f9..5a8d375 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
@@ -1,6 +1,6 @@
{
"framework": "qunit",
- "test_page": "tests/index.html",
+ "test_page": "tests/index.html?hidepassed&nocontainer",
"launch_in_ci": [
"PhantomJS"
],
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
index 46a73f9..8567f30 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
@@ -86,7 +86,7 @@ export default function() {
"fileResources":[],
"statusDir":"job1",
"id":1,
- "title":"New Query",
+ "title":"Worksheet",
"duration":2,
"forcedContent":"",
"owner":"admin",
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
index 6adbc89..b8928ff 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
@@ -46,6 +46,9 @@
zoom: 50%;
}
</style>
+
+ {{content-for 'head-footer'}}
+ {{content-for 'test-head-footer'}}
</head>
<body>
<div id="qunit"></div>
@@ -60,5 +63,8 @@
<script src="assets/blanket-loader.js"></script>
<script src="testem.js"></script>
<script src="assets/test-loader.js"></script>
+
+ {{content-for 'body-footer'}}
+ {{content-for 'test-body-footer'}}
</body>
</html>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
index 5be4fdc..0a6b12c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
@@ -58,7 +58,7 @@ test('Expanding a database will retrieve the first page of tables for that datab
click(targetDB);
andThen(function () {
- equal(find('.fa-th').length, 3);
+ equal(find('.fa-table').length, 3);
});
});
});
@@ -74,7 +74,7 @@ test('Expanding a table will retrieve the first page of columns for that table.'
click(targetDB);
andThen(function () {
- var targetTable = find('.fa-th').first();
+ var targetTable = find('.fa-table').first();
click(targetTable);
@@ -102,4 +102,4 @@ test('Searching for a table will display table results and column search field',
equal(find('.nav-tabs li').length, 2, 'Results tab has been redendered.');
});
});
-});
\ 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/tests/test-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
index 53d9f9b..96975ee 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
@@ -22,9 +22,3 @@ import {
} from 'ember-qunit';
setResolver(resolver);
-
-document.write('<div id="ember-testing-container"><div id="ember-testing"></div></div>');
-
-QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
-var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
-document.getElementById('ember-testing-container').style.visibility = containerVisibility;
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
index c7deabb..8f0f245 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
@@ -33,19 +33,19 @@ test('isExpanded is toggled on click', function() {
});
Ember.run(function() {
- component.click();
+ component.send('toggleMessage');
});
equal(component.get('message.isExpanded'), true, 'isExpanded is set to true');
Ember.run(function() {
- component.click();
+ component.send('toggleMessage');
});
equal(component.get('message.isExpanded'), false, 'isExpanded is set to false');
});
-test('removeLater should be called on click', function() {
+test('removeLater should be called when the message is toggled', function() {
expect(1);
var message = Ember.Object.create({ isExpanded: false});
@@ -63,11 +63,11 @@ test('removeLater should be called on click', function() {
});
Ember.run(function() {
- component.click();
+ component.send('toggleMessage');
});
Ember.run(function() {
- component.click();
+ component.send('toggleMessage');
});
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
index 291cc7e..7d7b531 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
@@ -26,7 +26,9 @@ moduleFor('controller:index', 'IndexController', {
'controller:index/history-query/results',
'controller:index/history-query/explain',
'controller:settings',
- 'adapter:database', 'controller:tables', 'controller:columns']
+ 'adapter:database', 'controller:tables', 'controller:columns',
+ 'controller:visual-explain', 'controller:tez-ui'
+ ]
});
test('when initialized, controller sets the queryProcessTabs.', function () {
@@ -106,14 +108,19 @@ test('bindQueryParams replaces same param multiple times', function() {
test('parseQueryParams sets queryParams when query changes', function() {
expect(3);
- var controller = this.subject();
- var query = "select $what from $where";
+ var query = Ember.Object.create({
+ id: 1,
+ fileContent: "select $what from $where"
+ });
+
+ var controller = this.subject({
+ model: query
+ });
Ember.run(function() {
- controller.set('openQueries.currentQuery', {
- 'fileContent': query
- });
+ controller.set('openQueries.queryTabs', [query]);
+ controller.set('openQueries.currentQuery', query);
});
equal(controller.get('queryParams.length'), 2, '2 queryParams parsed');
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
index d14f2cc..2578c33 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
@@ -20,7 +20,10 @@ import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:queries', 'QueriesController', {
- needs: [ 'controller:history' ]
+ needs: [
+ 'controller:history',
+ 'controller:open-queries'
+ ]
});
test('controller is initialized', function() {
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
index ef1b3d8..0101ceb 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
@@ -29,7 +29,12 @@ moduleFor('controller:settings', 'SettingsController', {
'controller:index/history-query/explain',
'controller:columns',
'controller:udfs',
- 'controller:index/history-query/logs'
+ 'controller:index/history-query/logs',
+ 'controller:visual-explain',
+ 'controller:tez-ui',
+ 'controller:tables',
+ 'adapter:database',
+ 'adapter:application'
]
});
@@ -48,6 +53,7 @@ test('can add a setting', function() {
test('hasSettings return true if there are settings', function() {
var controller = this.subject();
+ controller.get('currentSettings');
ok(!controller.hasSettings(null), 'No settings => return false');
Ember.run(function() {
@@ -81,12 +87,21 @@ test('validate', function() {
predefinedSettings: predefinedSettings
});
+ controller.set('openQueries.update', function () {
+ var defer = Ember.RSVP.defer();
+ defer.resolve();
+
+ return defer.promise;
+ });
+
var settings = [
Ember.Object.create({key: { name: 'some.key' }, value: 'value'}),
Ember.Object.create({key: { name: 'some.key' }, value: '123'})
];
Ember.run(function() {
+ controller.set('index.model', Ember.Object.create({ id: 1 }));
+ controller.get('currentSettings');
controller.setSettingForQuery(1, settings);
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
index f2755c5..e04abd5 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
@@ -23,7 +23,7 @@ import {
moduleFor('controller:tez-ui', 'TezUiController', {
// Specify the other units that are required for this test.
- // needs: ['controller:foo']
+ needs: ['controller:index']
});
// Replace this with your real tests.
[4/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain,
Error handling and bugfixes (alexantonenko)
Posted by al...@apache.org.
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('excluded.@each.key').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('currentSettings.settings.@each.key'),
+
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.[]', 'currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
+ }.observes('currentSettings.[]', 'currentSettings.settings.[]', 'currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
currentSettingsAreValid: function() {
var currentSettings = this.get('currentSettings.settings');
@@ -161,6 +179,24 @@ export default Ember.ArrayController.extend({
return invalid.length ? false : true;
}.property('currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
+ 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>
[5/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain,
Error handling and bugfixes (alexantonenko)
Posted by al...@apache.org.
AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/672eee34
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/672eee34
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/672eee34
Branch: refs/heads/trunk
Commit: 672eee34dce8200f7375f164c307d864b4f5ffd1
Parents: 2917d0d
Author: Alex Antonenko <hi...@gmail.com>
Authored: Fri Apr 17 18:39:50 2015 +0300
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Fri Apr 17 22:10:24 2015 +0300
----------------------------------------------------------------------
contrib/views/hive/pom.xml | 11 +-
.../ambari/view/hive/PropertyValidator.java | 109 +++++
.../ambari/view/hive/client/Connection.java | 44 +-
.../view/hive/client/ConnectionFactory.java | 7 +-
.../apache/ambari/view/hive/client/Cursor.java | 22 +-
.../ambari/view/hive/client/DDLDelegator.java | 2 +-
.../hive/client/HiveErrorStatusException.java | 2 +-
.../apache/ambari/view/hive/client/Utils.java | 2 +-
.../view/hive/persistence/DataStoreStorage.java | 114 +++---
.../persistence/InstanceKeyValueStorage.java | 5 -
.../hive/persistence/LocalKeyValueStorage.java | 5 -
.../ambari/view/hive/persistence/Storage.java | 9 -
.../hive/resources/CRUDResourceManager.java | 2 +-
.../view/hive/resources/files/FileService.java | 36 +-
.../view/hive/resources/jobs/Aggregator.java | 3 +-
.../resources/jobs/ConnectionController.java | 15 +-
.../view/hive/resources/jobs/JobService.java | 74 +++-
.../jobs/NoOperationStatusSetException.java | 5 +-
.../jobs/OperationHandleController.java | 45 ++-
.../jobs/OperationHandleResourceManager.java | 2 +-
.../jobs/ResultsPaginationController.java | 24 ++
.../resources/jobs/StoredOperationHandle.java | 2 +-
.../hive/resources/jobs/atsJobs/ATSParser.java | 1 +
.../jobs/atsJobs/ATSRequestsDelegate.java | 8 +
.../jobs/atsJobs/ATSRequestsDelegateImpl.java | 24 +-
.../resources/jobs/atsJobs/HiveQueryId.java | 2 +
.../view/hive/resources/jobs/viewJobs/Job.java | 8 +
.../resources/jobs/viewJobs/JobController.java | 1 +
.../jobs/viewJobs/JobControllerImpl.java | 39 +-
.../hive/resources/jobs/viewJobs/JobImpl.java | 28 +-
.../ambari/view/hive/resources/udfs/UDF.java | 6 +-
.../apache/ambari/view/hive/utils/HdfsApi.java | 6 +-
.../apache/ambari/view/hive/utils/HdfsUtil.java | 8 +-
.../utils/HiveClientFormattedException.java | 26 ++
.../src/main/resources/ui/hive-web/.bowerrc | 4 +
.../main/resources/ui/hive-web/.editorconfig | 34 ++
.../src/main/resources/ui/hive-web/.ember-cli | 27 ++
.../src/main/resources/ui/hive-web/.travis.yml | 38 ++
.../src/main/resources/ui/hive-web/Brocfile.js | 5 +
.../app/components/alert-message-widget.js | 16 +-
.../app/components/collapsible-widget.js | 5 +
.../ui/hive-web/app/components/modal-widget.js | 22 +
.../ui/hive-web/app/components/notify-widget.js | 32 ++
.../app/components/number-range-widget.js | 36 +-
.../ui/hive-web/app/components/query-tabs.js | 121 ++++++
.../hive-web/app/components/typeahead-widget.js | 49 ++-
.../ui/hive-web/app/controllers/alerts.js | 47 ---
.../ui/hive-web/app/controllers/databases.js | 56 ++-
.../ui/hive-web/app/controllers/index.js | 188 +++++++--
.../controllers/index/history-query/explain.js | 36 +-
.../app/controllers/index/history-query/logs.js | 22 +-
.../controllers/index/history-query/results.js | 34 ++
.../ui/hive-web/app/controllers/messages.js | 33 ++
.../app/controllers/modal-save-query.js | 42 ++
.../ui/hive-web/app/controllers/open-queries.js | 120 ++++--
.../ui/hive-web/app/controllers/queries.js | 11 +-
.../ui/hive-web/app/controllers/settings.js | 63 ++-
.../ui/hive-web/app/controllers/udf.js | 4 +-
.../ui/hive-web/app/helpers/all-uppercase.js | 2 +-
.../hive-web/app/helpers/preformatted-string.js | 28 ++
.../main/resources/ui/hive-web/app/index.html | 4 +
.../ui/hive-web/app/initializers/i18n.js | 43 +-
.../ui/hive-web/app/initializers/notify.js | 26 ++
.../resources/ui/hive-web/app/models/file.js | 2 +-
.../resources/ui/hive-web/app/models/job.js | 5 +
.../ui/hive-web/app/routes/application.js | 17 +-
.../ui/hive-web/app/routes/index/index.js | 4 +-
.../ui/hive-web/app/services/notify.js | 88 ++++
.../resources/ui/hive-web/app/styles/app.scss | 136 +++++--
.../ui/hive-web/app/styles/mixins.scss | 23 ++
.../ui/hive-web/app/styles/notifications.scss | 36 ++
.../ui/hive-web/app/styles/query-tabs.scss | 68 ++++
.../resources/ui/hive-web/app/styles/vars.scss | 20 +
.../ui/hive-web/app/templates/alerts.hbs | 23 --
.../ui/hive-web/app/templates/application.hbs | 3 +-
.../components/alert-message-widget.hbs | 4 +-
.../templates/components/collapsible-widget.hbs | 7 +-
.../app/templates/components/notify-widget.hbs | 21 +
.../app/templates/components/panel-widget.hbs | 12 +-
.../app/templates/components/query-tabs.hbs | 29 ++
.../app/templates/components/tabs-widget.hbs | 3 +-
.../app/templates/databases-search-results.hbs | 4 +-
.../hive-web/app/templates/databases-tree.hbs | 6 +-
.../ui/hive-web/app/templates/databases.hbs | 4 +-
.../ui/hive-web/app/templates/index.hbs | 24 +-
.../templates/index/history-query/results.hbs | 6 +-
.../ui/hive-web/app/templates/insert-udfs.hbs | 54 +--
.../ui/hive-web/app/templates/message.hbs | 36 ++
.../ui/hive-web/app/templates/messages.hbs | 30 ++
.../ui/hive-web/app/templates/modal-delete.hbs | 2 +-
.../hive-web/app/templates/modal-save-query.hbs | 24 ++
.../ui/hive-web/app/templates/notification.hbs | 23 ++
.../ui/hive-web/app/templates/open-queries.hbs | 2 +-
.../ui/hive-web/app/templates/settings.hbs | 79 ++--
.../ui/hive-web/app/templates/tez-ui.hbs | 2 +
.../hive-web/app/templates/visual-explain.hbs | 58 +++
.../ui/hive-web/app/utils/constants.js | 23 +-
.../ui/hive-web/app/utils/dag-rules.js | 141 +++++++
.../resources/ui/hive-web/app/views/message.js | 36 ++
.../ui/hive-web/app/views/notification.js | 51 +++
.../ui/hive-web/app/views/visual-explain.js | 401 +++++++++++++++++++
.../src/main/resources/ui/hive-web/bower.json | 18 +-
.../src/main/resources/ui/hive-web/package.json | 16 +-
.../src/main/resources/ui/hive-web/testem.json | 2 +-
.../ui/hive-web/tests/helpers/api-mock.js | 2 +-
.../main/resources/ui/hive-web/tests/index.html | 6 +
.../hive-web/tests/integration/database-test.js | 6 +-
.../resources/ui/hive-web/tests/test-helper.js | 6 -
.../components/alert-message-widget-test.js | 10 +-
.../tests/unit/controllers/index-test.js | 19 +-
.../tests/unit/controllers/queries-test.js | 5 +-
.../tests/unit/controllers/settings-test.js | 17 +-
.../tests/unit/controllers/tez-ui-test.js | 2 +-
.../resources/ui/hive-web/vendor/dagre.min.js | 27 ++
contrib/views/hive/src/main/resources/view.xml | 8 +-
.../view/hive/resources/jobs/ATSParserTest.java | 22 +-
.../hive/resources/jobs/AggregatorTest.java | 2 +-
pom.xml | 1 +
118 files changed, 2938 insertions(+), 583 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/pom.xml b/contrib/views/hive/pom.xml
index e381719..81bf144 100644
--- a/contrib/views/hive/pom.xml
+++ b/contrib/views/hive/pom.xml
@@ -19,7 +19,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.ambari.contrib.views</groupId>
<artifactId>hive</artifactId>
- <version>0.1.0-SNAPSHOT</version>
+ <version>0.2.0-SNAPSHOT</version>
<name>Hive</name>
<parent>
@@ -30,6 +30,11 @@
<dependencies>
<dependency>
+ <groupId>com.jayway.jsonpath</groupId>
+ <artifactId>json-path</artifactId>
+ <version>2.0.0</version>
+ </dependency>
+ <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
@@ -195,8 +200,8 @@
<artifactId>frontend-maven-plugin</artifactId>
<version>0.0.14</version>
<configuration>
- <nodeVersion>v0.10.32</nodeVersion>
- <npmVersion>1.4.3</npmVersion>
+ <nodeVersion>v0.12.2</nodeVersion>
+ <npmVersion>1.4.8</npmVersion>
<workingDirectory>src/main/resources/ui/hive-web/</workingDirectory>
</configuration>
<executions>
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
new file mode 100644
index 0000000..61efa49
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
@@ -0,0 +1,109 @@
+/**
+ * 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.hive;
+
+import org.apache.ambari.view.ViewInstanceDefinition;
+import org.apache.ambari.view.validation.ValidationResult;
+import org.apache.ambari.view.validation.Validator;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class PropertyValidator implements Validator {
+
+ public static final String WEBHDFS_URL = "webhdfs.url";
+ public static final String HIVE_PORT = "hive.port";
+ public static final String YARN_ATS_URL = "yarn.ats.url";
+ public static final String HIVE_AUTH = "hive.auth";
+
+ @Override
+ public ValidationResult validateInstance(ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+ return null;
+ }
+
+ @Override
+ public ValidationResult validateProperty(String property, ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+ if (property.equals(WEBHDFS_URL)) {
+ String webhdfsUrl = viewInstanceDefinition.getPropertyMap().get(WEBHDFS_URL);
+ if (validateURL(webhdfsUrl)) return new InvalidPropertyValidationResult(false, "Must be valid URL");
+ }
+
+ if (property.equals(HIVE_PORT)) {
+ String hivePort = viewInstanceDefinition.getPropertyMap().get(HIVE_PORT);
+ try {
+ int port = Integer.valueOf(hivePort);
+ if (port < 1 || port > 65535) {
+ return new InvalidPropertyValidationResult(false, "Must be from 1 to 65535");
+ }
+ } catch (NumberFormatException e) {
+ return new InvalidPropertyValidationResult(false, "Must be integer");
+ }
+ }
+
+ if (property.equals(YARN_ATS_URL)) {
+ String atsUrl = viewInstanceDefinition.getPropertyMap().get(YARN_ATS_URL);
+ if (validateURL(atsUrl)) return new InvalidPropertyValidationResult(false, "Must be valid URL");
+ }
+
+ if (property.equals(HIVE_AUTH)) {
+ String auth = viewInstanceDefinition.getPropertyMap().get(HIVE_AUTH);
+
+ if (auth != null && !auth.isEmpty()) {
+ for(String param : auth.split(";")) {
+ String[] keyvalue = param.split("=");
+ if (keyvalue.length != 2) {
+ return new InvalidPropertyValidationResult(false, "Can not parse authentication param " + param + " in " + auth);
+ }
+ }
+ }
+ }
+
+ return ValidationResult.SUCCESS;
+ }
+
+ public boolean validateURL(String webhdfsUrl) {
+ try {
+ new URI(webhdfsUrl);
+ } catch (URISyntaxException e) {
+ return true;
+ }
+ return false;
+ }
+
+ public static class InvalidPropertyValidationResult implements ValidationResult {
+ private boolean valid;
+ private String detail;
+
+ public InvalidPropertyValidationResult(boolean valid, String detail) {
+ this.valid = valid;
+ this.detail = detail;
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ }
+
+ @Override
+ public String getDetail() {
+ return detail;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
index 70b5047..cf1a998 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
@@ -82,7 +82,7 @@ public class Connection {
transport.open();
client = new TCLIService.Client(new TBinaryProtocol(transport));
} catch (TTransportException e) {
- throw new HiveClientException("Could not establish connecton to "
+ throw new HiveClientException("H020 Could not establish connecton to "
+ host + ":" + port + ": " + e.toString(), e);
}
LOG.info("Hive connection opened");
@@ -109,7 +109,7 @@ public class Connection {
try {
saslQOP = SaslQOP.fromString(authParams.get(Utils.HiveAuthenticationParams.AUTH_QOP));
} catch (IllegalArgumentException e) {
- throw new HiveClientException("Invalid " + Utils.HiveAuthenticationParams.AUTH_QOP +
+ throw new HiveClientException("H040 Invalid " + Utils.HiveAuthenticationParams.AUTH_QOP +
" parameter. " + e.getMessage(), e);
}
}
@@ -162,7 +162,7 @@ public class Connection {
return HiveAuthFactory.getSocketTransport(host, port, 10000);
}
} catch (SaslException e) {
- throw new HiveClientException("Could not create secure connection to "
+ throw new HiveClientException("H040 Could not create secure connection to "
+ host + ": " + e.getMessage(), e);
}
return transport;
@@ -181,7 +181,7 @@ public class Connection {
tokenStr = ShimLoader.getHadoopShims().
getTokenStrForm(HiveAuthFactory.HS2_CLIENT_TOKEN);
} catch (IOException e) {
- throw new HiveClientException("Error reading token ", e);
+ throw new HiveClientException("H050 Error reading token", e);
}
}
return tokenStr;
@@ -205,12 +205,12 @@ public class Connection {
try {
return client.OpenSession(openReq);
} catch (TException e) {
- throw new HiveClientException("Unable to open Hive session", e);
+ throw new HiveClientException("H060 Unable to open Hive session", e);
}
}
}.call();
- Utils.verifySuccess(openResp.getStatus(), "Unable to open Hive session");
+ Utils.verifySuccess(openResp.getStatus(), "H070 Unable to open Hive session");
if (protocol == null)
protocol = openResp.getServerProtocolVersion();
@@ -231,7 +231,7 @@ public class Connection {
public TSessionHandle getSessionByTag(String tag) throws HiveClientException {
TSessionHandle sessionHandle = sessHandles.get(tag);
if (sessionHandle == null) {
- throw new HiveClientException("Session with provided tag not found", null);
+ throw new HiveClientException("E030 Session with provided tag not found", null);
}
return sessionHandle;
}
@@ -244,15 +244,21 @@ public class Connection {
}
}
+ public void invalidateSessionByTag(String tag) throws HiveClientException {
+ TSessionHandle sessionHandle = getSessionByTag(tag);
+ closeSession(sessionHandle);
+ sessHandles.remove(tag);
+ }
+
private synchronized void closeSession(TSessionHandle sessHandle) throws HiveClientException {
if (sessHandle == null) return;
TCloseSessionReq closeReq = new TCloseSessionReq(sessHandle);
TCloseSessionResp closeResp = null;
try {
closeResp = client.CloseSession(closeReq);
- Utils.verifySuccess(closeResp.getStatus(), "Unable to close Hive session");
+ Utils.verifySuccess(closeResp.getStatus(), "H080 Unable to close Hive session");
} catch (TException e) {
- throw new HiveClientException("Unable to close Hive session", e);
+ throw new HiveClientException("H090 Unable to close Hive session", e);
}
LOG.info("Hive session closed");
}
@@ -314,18 +320,18 @@ public class Connection {
try {
return client.ExecuteStatement(execReq);
} catch (TException e) {
- throw new HiveClientException("Unable to submit statement " + cmd, e);
+ throw new HiveClientException("H100 Unable to submit statement " + cmd, e);
}
}
}.call();
- Utils.verifySuccess(execResp.getStatus(), "Unable to submit statement " + cmd);
+ Utils.verifySuccess(execResp.getStatus(), "H110 Unable to submit statement");
//TODO: check if status have results
handle = execResp.getOperationHandle();
}
if (handle == null) {
- throw new HiveClientException("Empty command given", null);
+ throw new HiveClientException("H120 Empty command given", null);
}
return handle;
}
@@ -373,19 +379,11 @@ public class Connection {
try {
return client.GetOperationStatus(statusReq);
} catch (TException e) {
- throw new HiveClientException("Unable to fetch operation status", e);
+ throw new HiveClientException("H130 Unable to fetch operation status", e);
}
}
}.call();
-// transportLock.lock();
-// try {
-// return client.GetOperationStatus(statusReq);
-// } catch (TException e) {
-// throw new HiveClientException("Unable to fetch operation status", e);
-// } finally {
-// transportLock.unlock();
-// }
}
/**
@@ -400,11 +398,11 @@ public class Connection {
try {
return client.CancelOperation(cancelReq);
} catch (TException e) {
- throw new HiveClientException("Unable to cancel operation", null);
+ throw new HiveClientException("H140 Unable to cancel operation", null);
}
}
}.call();
- Utils.verifySuccess(cancelResp.getStatus(), "Unable to cancel operation");
+ Utils.verifySuccess(cancelResp.getStatus(), "H150 Unable to cancel operation");
}
public int getPort() {
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
index 6886f57..98256eb 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
@@ -19,6 +19,7 @@
package org.apache.ambari.view.hive.client;
import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
import org.apache.ambari.view.hive.utils.ServiceFormattedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,7 +42,7 @@ public class ConnectionFactory implements IConnectionFactory {
return new Connection(getHiveHost(), Integer.valueOf(getHivePort()),
getHiveAuthParams(), context.getUsername());
} catch (HiveClientException e) {
- throw new ServiceFormattedException("Couldn't open connection to Hive: " + e.toString(), e);
+ throw new HiveClientFormattedException(e);
}
}
@@ -62,8 +63,8 @@ public class ConnectionFactory implements IConnectionFactory {
for(String param : auth.split(";")) {
String[] keyvalue = param.split("=");
if (keyvalue.length != 2) {
- LOG.error("Can not parse authentication param " + param + " in " + auth);
- continue;
+ //Should never happen because validator already checked this
+ throw new ServiceFormattedException("H010 Can not parse authentication param " + param + " in " + auth);
}
params.put(keyvalue[0], keyvalue[1]);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
index 84987f5..16fdf36 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
@@ -21,6 +21,7 @@ package org.apache.ambari.view.hive.client;
import static org.apache.hive.service.cli.thrift.TCLIServiceConstants.TYPE_NAMES;
import org.apache.ambari.view.hive.utils.BadRequestFormattedException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
import org.apache.hive.service.cli.RowSet;
import org.apache.hive.service.cli.RowSetFactory;
import org.apache.hive.service.cli.thrift.*;
@@ -73,12 +74,12 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
try {
return client.FetchResults(fetchReq);
} catch (TException e) {
- throw new HiveClientException("Unable to fetch results", e);
+ throw new HiveClientException("H160 Unable to fetch results", e);
}
}
}.call();
- Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results");
+ Utils.verifySuccess(fetchResp.getStatus(), "H170 Unable to fetch results");
TRowSet results = fetchResp.getResults();
fetched = RowSetFactory.create(results, connection.getProtocol());
fetchedIterator = fetched.iterator();
@@ -90,7 +91,6 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
public ArrayList<ColumnDescription> getSchema() throws HiveClientException {
if (this.schema == null) {
- // TODO: extract all HiveCall inline classes to separate files
TGetResultSetMetadataResp fetchResp = new HiveCall<TGetResultSetMetadataResp>(connection) {
@Override
public TGetResultSetMetadataResp body() throws HiveClientException {
@@ -99,12 +99,12 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
try {
return client.GetResultSetMetadata(fetchReq);
} catch (TException e) {
- throw new HiveClientException("Unable to fetch results metadata", e);
+ throw new HiveClientException("H180 Unable to fetch results metadata", e);
}
}
}.call();
- Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results metadata");
+ Utils.verifySuccess(fetchResp.getStatus(), "H190 Unable to fetch results metadata");
TTableSchema schema = fetchResp.getSchema();
List<TColumnDesc> thriftColumns = schema.getColumns();
@@ -168,7 +168,7 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
try {
fetchNextBlock();
} catch (HiveClientException e) {
- throw new HiveClientRuntimeException(e.getMessage(), e);
+ throw new HiveClientFormattedException(e);
}
}
}
@@ -209,6 +209,16 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
return read;
}
+ public Row getHeadersRow() throws HiveClientException {
+ ArrayList<ColumnDescription> schema = getSchema();
+
+ Object[] row = new Object[schema.size()];
+ for (ColumnDescription columnDescription : schema) {
+ row[columnDescription.getPosition()-1] = columnDescription.getName();
+ }
+ return new Row(row, selectedColumns);
+ }
+
public int readRaw(ArrayList<Object[]> rows, int count) {
int read = 0;
while(read < count && hasNext()) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
index 8326e50..eda12ed 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
@@ -129,7 +129,7 @@ public class DDLDelegator {
try {
return connection.getClient().GetColumns(req);
} catch (TException e) {
- throw new HiveClientException("Unable to get table columns", e);
+ throw new HiveClientException("H200 Unable to get table columns", e);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
index 7adbe23..1b306dc 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
@@ -25,6 +25,6 @@ import org.apache.hive.service.cli.thrift.TStatusCode;
*/
public class HiveErrorStatusException extends HiveClientException {
public HiveErrorStatusException(TStatusCode statusCode, String comment) {
- super(String.format("Failed with status %s: %s", statusCode, comment), null);
+ super(String.format("%s [%s]", comment, statusCode), null);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
index fd1ecb5..e0dd438 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
@@ -26,7 +26,7 @@ public class Utils {
if (status.getStatusCode() != TStatusCode.SUCCESS_STATUS &&
status.getStatusCode() != TStatusCode.SUCCESS_WITH_INFO_STATUS) {
String message = (status.getErrorMessage() != null) ? status.getErrorMessage() : "";
- throw new HiveErrorStatusException(status.getStatusCode(), message + ": " + comment);
+ throw new HiveErrorStatusException(status.getStatusCode(), comment + ". " + message);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
index b4bc415..1e8f07f 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
@@ -25,10 +25,14 @@ import org.apache.ambari.view.hive.persistence.utils.Indexed;
import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.WebApplicationException;
+import java.beans.Transient;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -52,14 +56,52 @@ public class DataStoreStorage implements Storage {
@Override
public synchronized void store(Class model, Indexed obj) {
+ assignId(model, obj);
+
+ Indexed newBean;
try {
- if (obj.getId() == null) {
- String id = nextIdForEntity(context, model);
- obj.setId(id);
- }
- context.getDataStore().store(obj);
+ newBean = (Indexed) BeanUtils.cloneBean(obj);
+ } catch (IllegalAccessException e) {
+ throw new ServiceFormattedException("S010 Data storage error", e);
+ } catch (InstantiationException e) {
+ throw new ServiceFormattedException("S010 Data storage error", e);
+ } catch (InvocationTargetException e) {
+ throw new ServiceFormattedException("S010 Data storage error", e);
+ } catch (NoSuchMethodException e) {
+ throw new ServiceFormattedException("S010 Data storage error", e);
+ }
+ preprocessEntity(newBean);
+
+ try {
+ context.getDataStore().store(newBean);
} catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while saving object to DataStorage", e);
+ throw new ServiceFormattedException("S020 Data storage error", e);
+ }
+ }
+
+ public void assignId(Class model, Indexed obj) {
+ if (obj.getId() == null) {
+ String id = nextIdForEntity(context, model);
+ obj.setId(id);
+ }
+ }
+
+ private void preprocessEntity(Indexed obj) {
+ cleanTransientFields(obj);
+ }
+
+ private void cleanTransientFields(Indexed obj) {
+ for (Method m : obj.getClass().getMethods()) {
+ Transient aTransient = m.getAnnotation(Transient.class);
+ if (aTransient != null && m.getName().startsWith("set")) {
+ try {
+ m.invoke(obj, new Object[]{ null });
+ } catch (IllegalAccessException e) {
+ throw new ServiceFormattedException("S030 Data storage error", e);
+ } catch (InvocationTargetException e) {
+ throw new ServiceFormattedException("S030 Data storage error", e);
+ }
+ }
}
}
@@ -87,7 +129,7 @@ public class DataStoreStorage implements Storage {
throw new ItemNotFound();
}
} catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while finding object in DataStorage", e);
+ throw new ServiceFormattedException("S040 Data storage error", e);
}
}
@@ -96,27 +138,16 @@ public class DataStoreStorage implements Storage {
LinkedList<T> list = new LinkedList<T>();
LOG.debug(String.format("Loading all %s-s", model.getName()));
try {
- //TODO: use WHERE statement instead of this ugly filter
for(T item: context.getDataStore().findAll(model, filter.whereStatement())) {
list.add(item);
}
} catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while finding all objects in DataStorage", e);
+ throw new ServiceFormattedException("S050 Data storage error", e);
}
return list;
}
@Override
- public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
- LOG.debug(String.format("Loading all %s-s", model.getName()));
- try {
- return new ArrayList<T>(context.getDataStore().findAll(model, where));
- } catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while finding objects in DataStorage; where = " + where, e);
- }
- }
-
- @Override
public synchronized <T extends Indexed> List<T> loadAll(Class<T> model) {
return loadAll(model, new OnlyOwnersFilteringStrategy(this.context.getUsername()));
}
@@ -128,7 +159,7 @@ public class DataStoreStorage implements Storage {
try {
context.getDataStore().remove(obj);
} catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while removing object from DataStorage", e);
+ throw new ServiceFormattedException("S060 Data storage error", e);
}
}
@@ -137,48 +168,7 @@ public class DataStoreStorage implements Storage {
try {
return context.getDataStore().find(model, id) != null;
} catch (PersistenceException e) {
- throw new ServiceFormattedException("Error while finding object in DataStorage", e);
- }
- }
-
- public static void storageSmokeTest(ViewContext context) {
- try {
- SmokeTestEntity entity = new SmokeTestEntity();
- entity.setData("42");
- DataStoreStorage storage = new DataStoreStorage(context);
- storage.store(SmokeTestEntity.class, entity);
-
- if (entity.getId() == null) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly (auto increment id doesn't work)", null);
- Object id = entity.getId();
- SmokeTestEntity entity2 = storage.load(SmokeTestEntity.class, id);
- boolean status = entity2.getData().compareTo("42") == 0;
- storage.delete(SmokeTestEntity.class, id);
- if (!status) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly", null);
- } catch (WebApplicationException ex) {
- throw ex;
- } catch (Exception ex) {
- throw new ServiceFormattedException(ex.getMessage(), ex);
- }
- }
-
- public static class SmokeTestEntity implements Indexed {
- private String id = null;
- private String data = null;
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getData() {
- return data;
- }
-
- public void setData(String data) {
- this.data = data;
+ throw new ServiceFormattedException("S070 Data storage error", e);
}
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
index 932b58e..98703fa 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
@@ -132,9 +132,4 @@ public class InstanceKeyValueStorage extends KeyValueStorage {
throw new ServiceFormattedException(ex.getMessage(), ex);
}
}
-
- @Override
- public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
- throw new NotImplementedException();
- }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
index 674029a..24ed335 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
@@ -70,9 +70,4 @@ public class LocalKeyValueStorage extends KeyValueStorage {
}
return config;
}
-
- @Override
- public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
- throw new NotImplementedException();
- }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
index 188282e..a34f566 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
@@ -55,15 +55,6 @@ public interface Storage {
/**
* Load all objects of given bean class
* @param model bean class
- * @param where filtering strategy (where clause)
- * @param <T> bean class
- * @return list of filtered objects
- */
- <T extends Indexed> List<T> loadWhere(Class<T> model, String where);
-
- /**
- * Load all objects of given bean class
- * @param model bean class
* @param <T> bean class
* @return list of all objects
*/
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
index 891b526..c7167a8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
@@ -124,7 +124,7 @@ abstract public class CRUDResourceManager<T extends Indexed> implements IResourc
try {
delete(object.getId());
} catch (ItemNotFound itemNotFound) {
- throw new ServiceFormattedException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
+ throw new ServiceFormattedException("E040 Item not found", itemNotFound);
}
throw e;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
index 3f5b3b8..8bde44c 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
@@ -19,11 +19,13 @@
package org.apache.ambari.view.hive.resources.files;
import com.google.inject.Inject;
+import com.jayway.jsonpath.JsonPath;
import org.apache.ambari.view.ViewContext;
import org.apache.ambari.view.ViewResourceHandler;
import org.apache.ambari.view.hive.BaseService;
import org.apache.ambari.view.hive.utils.*;
import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.json.simple.JSONObject;
@@ -38,6 +40,9 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
/**
* File access resource
@@ -53,6 +58,7 @@ import java.io.IOException;
*/
public class FileService extends BaseService {
public static final String FAKE_FILE = "fakefile://";
+ public static final String JSON_PATH_FILE = "jsonpath:";
@Inject
ViewResourceHandler handler;
@@ -78,10 +84,17 @@ public class FileService extends BaseService {
if (page > 1)
throw new IllegalArgumentException("There's only one page in fake files");
- String content = filePath.substring(FAKE_FILE.length());
+ String encodedContent = filePath.substring(FAKE_FILE.length());
+ String content = new String(Base64.decodeBase64(encodedContent));
fillFakeFileObject(filePath, file, content);
- } else {
+ } else if (filePath.startsWith(JSON_PATH_FILE)) {
+ if (page > 1)
+ throw new IllegalArgumentException("There's only one page in fake files");
+
+ String content = getJsonPathContentByUrl(filePath);
+ fillFakeFileObject(filePath, file, content);
+ } else {
FilePaginator paginator = new FilePaginator(filePath, getSharedObjectsFactory().getHdfsApi());
fillRealFileObject(filePath, page, file, paginator);
@@ -101,6 +114,19 @@ public class FileService extends BaseService {
}
}
+ protected String getJsonPathContentByUrl(String filePath) throws IOException {
+ URL url = new URL(filePath.substring(JSON_PATH_FILE.length()));
+
+ InputStream responseInputStream = context.getURLStreamProvider().readFrom(url.toString(), "GET",
+ null, new HashMap<String, String>());
+ String response = IOUtils.toString(responseInputStream);
+
+ for (String ref : url.getRef().split("!")) {
+ response = JsonPath.read(response, ref);
+ }
+ return response;
+ }
+
public void fillRealFileObject(String filePath, Long page, FileResource file, FilePaginator paginator) throws IOException, InterruptedException {
file.setFilePath(filePath);
file.setFileContent(paginator.readPage(page));
@@ -109,9 +135,7 @@ public class FileService extends BaseService {
file.setPageCount(paginator.pageCount());
}
- public void fillFakeFileObject(String filePath, FileResource file, String encodedContent) {
- String content = new String(Base64.decodeBase64(encodedContent));
-
+ public void fillFakeFileObject(String filePath, FileResource file, String content) {
file.setFilePath(filePath);
file.setFileContent(content);
file.setHasNext(false);
@@ -176,7 +200,7 @@ public class FileService extends BaseService {
}
output.close();
} catch (FileAlreadyExistsException ex) {
- throw new ServiceFormattedException(ex.getMessage(), ex, 400);
+ throw new ServiceFormattedException("F020 File already exists", ex, 400);
}
response.setHeader("Location",
String.format("%s/%s", ui.getAbsolutePath().toString(), request.file.getFilePath()));
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
index e5fae95..687298e 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
@@ -23,6 +23,7 @@ import org.apache.ambari.view.hive.persistence.utils.Indexed;
import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
import org.apache.ambari.view.hive.resources.IResourceManager;
+import org.apache.ambari.view.hive.resources.files.FileService;
import org.apache.ambari.view.hive.resources.jobs.atsJobs.HiveQueryId;
import org.apache.ambari.view.hive.resources.jobs.atsJobs.IATSParser;
import org.apache.ambari.view.hive.resources.jobs.atsJobs.TezDagId;
@@ -192,7 +193,7 @@ public class Aggregator {
String query = atsHiveQuery.query;
atsJob.setTitle(query.substring(0, (query.length() > 42)?42:query.length()));
- atsJob.setQueryFile("fakefile://" + Base64.encodeBase64URLSafeString(query.getBytes())); // fake queryFile
+ atsJob.setQueryFile(FileService.JSON_PATH_FILE + atsHiveQuery.url + "#otherinfo.QUERY!queryText");
return atsJob;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
index c52d7b5..f481993 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
@@ -20,6 +20,7 @@ package org.apache.ambari.view.hive.resources.jobs;
import org.apache.ambari.view.hive.client.Connection;
import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
import org.apache.ambari.view.hive.utils.ServiceFormattedException;
import org.apache.commons.codec.binary.Hex;
import org.apache.hive.service.cli.thrift.TOperationHandle;
@@ -35,12 +36,8 @@ public class ConnectionController {
this.operationHandleControllerFactory = operationHandleControllerFactory;
}
- public TSessionHandle getSessionByTag(String tag) {
- try {
- return connection.getSessionByTag(tag);
- } catch (HiveClientException e) {
- throw new ServiceFormattedException(e.toString(), e);
- }
+ public TSessionHandle getSessionByTag(String tag) throws HiveClientException {
+ return connection.getSessionByTag(tag);
}
public String openSession() {
@@ -48,7 +45,7 @@ public class ConnectionController {
TSessionHandle sessionHandle = connection.openSession();
return getTagBySession(sessionHandle);
} catch (HiveClientException e) {
- throw new ServiceFormattedException(e.toString(), e);
+ throw new HiveClientFormattedException(e);
}
}
@@ -60,7 +57,7 @@ public class ConnectionController {
try {
connection.executeSync(session, "use " + database + ";");
} catch (HiveClientException e) {
- throw new ServiceFormattedException(e.toString(), e);
+ throw new HiveClientFormattedException(e);
}
}
@@ -69,7 +66,7 @@ public class ConnectionController {
try {
operationHandle = connection.executeAsync(session, cmd);
} catch (HiveClientException e) {
- throw new ServiceFormattedException(e.toString(), e);
+ throw new HiveClientFormattedException(e);
}
return operationHandleControllerFactory.createControllerForHandle(operationHandle);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
index 1a6f7bf..3c1895b 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
@@ -22,7 +22,9 @@ import com.google.inject.Inject;
import org.apache.ambari.view.ViewResourceHandler;
import org.apache.ambari.view.hive.BaseService;
import org.apache.ambari.view.hive.backgroundjobs.BackgroundJobController;
+import org.apache.ambari.view.hive.client.Connection;
import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.client.HiveClientException;
import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
import org.apache.ambari.view.hive.resources.jobs.atsJobs.ATSRequestsDelegate;
import org.apache.ambari.view.hive.resources.jobs.atsJobs.ATSRequestsDelegateImpl;
@@ -125,7 +127,7 @@ public class JobService extends BaseService {
try {
mergedJob = getAggregator().readATSJob(hiveJob);
} catch (ItemNotFound itemNotFound) {
- throw new ServiceFormattedException("Job not found", itemNotFound);
+ throw new ServiceFormattedException("E010 Job not found", itemNotFound);
}
Map createdJobMap = PropertyUtils.describe(mergedJob);
createdJobMap.remove("class"); // no need to show Bean class on client
@@ -143,6 +145,7 @@ public class JobService extends BaseService {
@Produces("text/csv")
public Response getResultsCSV(@PathParam("jobId") String jobId,
@Context HttpServletResponse response,
+ @QueryParam("fileName") String fileName,
@QueryParam("columns") final String requestedColumns) {
try {
JobController jobController = getResourceManager().readController(jobId);
@@ -155,6 +158,13 @@ public class JobService extends BaseService {
Writer writer = new BufferedWriter(new OutputStreamWriter(os));
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
try {
+
+ try {
+ csvPrinter.printRecord(resultSet.getHeadersRow().getRow());
+ } catch (HiveClientException e) {
+ LOG.error("Error on reading results header", e);
+ }
+
while (resultSet.hasNext()) {
csvPrinter.printRecord(resultSet.next().getRow());
writer.flush();
@@ -165,7 +175,13 @@ public class JobService extends BaseService {
}
};
- return Response.ok(stream).build();
+ if (fileName == null || fileName.isEmpty()) {
+ fileName = "results.csv";
+ }
+
+ return Response.ok(stream).
+ header("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)).
+ build();
} catch (WebApplicationException ex) {
throw ex;
} catch (ItemNotFound itemNotFound) {
@@ -216,11 +232,11 @@ public class JobService extends BaseService {
stream.close();
} catch (IOException e) {
- throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+ throw new ServiceFormattedException("F010 Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
} catch (InterruptedException e) {
- throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+ throw new ServiceFormattedException("F010 Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
} catch (ItemNotFound itemNotFound) {
- throw new NotFoundFormattedException("Job results are expired", itemNotFound);
+ throw new NotFoundFormattedException("E020 Job results are expired", itemNotFound);
}
}
@@ -261,6 +277,8 @@ public class JobService extends BaseService {
@QueryParam("columns") final String requestedColumns) {
try {
final JobController jobController = getResourceManager().readController(jobId);
+ if (!jobController.hasResults())
+ return ResultsPaginationController.emptyResponse().build();
return ResultsPaginationController.getInstance(context)
.request(jobId, searchId, true, fromBeginning, count,
@@ -385,6 +403,52 @@ public class JobService extends BaseService {
}
/**
+ * Invalidate session
+ */
+ @DELETE
+ @Path("sessions/{sessionTag}")
+ public Response invalidateSession(@PathParam("sessionTag") String sessionTag) {
+ try {
+ Connection connection = getSharedObjectsFactory().getHiveConnection();
+ connection.invalidateSessionByTag(sessionTag);
+ return Response.ok().build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Session status
+ */
+ @GET
+ @Path("sessions/{sessionTag}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response sessionStatus(@PathParam("sessionTag") String sessionTag) {
+ try {
+ Connection connection = getSharedObjectsFactory().getHiveConnection();
+
+ JSONObject session = new JSONObject();
+ session.put("sessionTag", sessionTag);
+ try {
+ connection.getSessionByTag(sessionTag);
+ session.put("actual", true);
+ } catch (HiveClientException ex) {
+ session.put("actual", false);
+ }
+
+ JSONObject status = new JSONObject();
+ status.put("session", session);
+ return Response.ok(status).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
* Wrapper object for json mapping
*/
public static class JobRequest {
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
index 1c1ce91..020c63e 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
@@ -19,8 +19,5 @@
package org.apache.ambari.view.hive.resources.jobs;
-public class NoOperationStatusSetException extends Throwable {
- public NoOperationStatusSetException(String s) {
- super(s);
- }
+public class NoOperationStatusSetException extends Exception {
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
index e146d55..34fc2d9 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
@@ -23,7 +23,7 @@ import org.apache.ambari.view.hive.client.Cursor;
import org.apache.ambari.view.hive.client.HiveClientException;
import org.apache.ambari.view.hive.client.IConnectionFactory;
import org.apache.ambari.view.hive.resources.jobs.viewJobs.Job;
-import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
import org.apache.hive.service.cli.thrift.TGetOperationStatusResp;
import org.apache.hive.service.cli.thrift.TOperationHandle;
import org.slf4j.Logger;
@@ -51,49 +51,54 @@ public class OperationHandleController {
this.operationHandle = storedOperationHandle;
}
- public String getOperationStatus() throws NoOperationStatusSetException, HiveClientException {
+ public OperationStatus getOperationStatus() throws NoOperationStatusSetException, HiveClientException {
TGetOperationStatusResp statusResp = connectionsFabric.getHiveConnection().getOperationStatus(operationHandle);
+
if (!statusResp.isSetOperationState()) {
- throw new NoOperationStatusSetException("Operation state is not set");
+ throw new NoOperationStatusSetException();
}
- String status;
+ OperationStatus opStatus = new OperationStatus();
+ opStatus.sqlState = statusResp.getSqlState();
+ opStatus.message = statusResp.getErrorMessage();
+
switch (statusResp.getOperationState()) {
case INITIALIZED_STATE:
- status = Job.JOB_STATE_INITIALIZED;
+ opStatus.status = Job.JOB_STATE_INITIALIZED;
break;
case RUNNING_STATE:
- status = Job.JOB_STATE_RUNNING;
+ opStatus.status = Job.JOB_STATE_RUNNING;
break;
case FINISHED_STATE:
- status = Job.JOB_STATE_FINISHED;
+ opStatus.status = Job.JOB_STATE_FINISHED;
break;
case CANCELED_STATE:
- status = Job.JOB_STATE_CANCELED;
+ opStatus.status = Job.JOB_STATE_CANCELED;
break;
case CLOSED_STATE:
- status = Job.JOB_STATE_CLOSED;
+ opStatus.status = Job.JOB_STATE_CLOSED;
break;
case ERROR_STATE:
- status = Job.JOB_STATE_ERROR;
+ opStatus.status = Job.JOB_STATE_ERROR;
break;
case UKNOWN_STATE:
- status = Job.JOB_STATE_UNKNOWN;
+ opStatus.status = Job.JOB_STATE_UNKNOWN;
break;
case PENDING_STATE:
- status = Job.JOB_STATE_PENDING;
+ opStatus.status = Job.JOB_STATE_PENDING;
break;
default:
- throw new NoOperationStatusSetException("Unknown status " + statusResp.getOperationState());
+ throw new NoOperationStatusSetException();
}
- return status;
+
+ return opStatus;
}
public void cancel() {
try {
connectionsFabric.getHiveConnection().cancelOperation(operationHandle);
} catch (HiveClientException e) {
- throw new ServiceFormattedException("Cancel failed: " + e.toString(), e);
+ throw new HiveClientFormattedException(e);
}
}
@@ -108,4 +113,14 @@ public class OperationHandleController {
public Cursor getResults() {
return connectionsFabric.getHiveConnection().getResults(operationHandle);
}
+
+ public boolean hasResults() {
+ return operationHandle.isHasResultSet();
+ }
+
+ public static class OperationStatus {
+ public String status;
+ public String sqlState;
+ public String message;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
index bab30a0..9e49dd1 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
@@ -65,7 +65,7 @@ public class OperationHandleResourceManager extends SharedCRUDResourceManager<St
try {
update(handle, jobRelatedHandles.get(0).getId());
} catch (ItemNotFound itemNotFound) {
- throw new ServiceFormattedException("Error when updating operation handle: " + itemNotFound.toString(), itemNotFound);
+ throw new ServiceFormattedException("E050 Error when updating operation handle: " + itemNotFound.toString(), itemNotFound);
}
} else {
create(handle);
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
index c152b03..8305708 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
@@ -23,6 +23,7 @@ import org.apache.ambari.view.ViewContext;
import org.apache.ambari.view.hive.client.ColumnDescription;
import org.apache.ambari.view.hive.client.HiveClientException;
import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
import org.apache.ambari.view.hive.utils.ServiceFormattedException;
import org.apache.commons.collections4.map.PassiveExpiringMap;
@@ -96,6 +97,8 @@ public class ResultsPaginationController {
Cursor resultSet = null;
try {
resultSet = makeResultsSet.call();
+ } catch (HiveClientException ex) {
+ throw new HiveClientFormattedException(ex);
} catch (Exception ex) {
throw new ServiceFormattedException(ex.getMessage(), ex);
}
@@ -127,6 +130,18 @@ public class ResultsPaginationController {
resultsResponse.setHasNext(resultSet.hasNext());
// resultsResponse.setSize(resultSet.size());
resultsResponse.setOffset(resultSet.getOffset());
+ resultsResponse.setHasResults(true);
+ return Response.ok(resultsResponse);
+ }
+
+ public static Response.ResponseBuilder emptyResponse() {
+ ResultsResponse resultsResponse = new ResultsResponse();
+ resultsResponse.setSchema(new ArrayList<ColumnDescription>());
+ resultsResponse.setRows(new ArrayList<Object[]>());
+ resultsResponse.setReadCount(0);
+ resultsResponse.setHasNext(false);
+ resultsResponse.setOffset(0);
+ resultsResponse.setHasResults(false);
return Response.ok(resultsResponse);
}
@@ -136,6 +151,7 @@ public class ResultsPaginationController {
private int readCount;
private boolean hasNext;
private long offset;
+ private boolean hasResults;
public void setSchema(ArrayList<ColumnDescription> schema) {
this.schema = schema;
@@ -176,5 +192,13 @@ public class ResultsPaginationController {
public void setOffset(long offset) {
this.offset = offset;
}
+
+ public boolean getHasResults() {
+ return hasResults;
+ }
+
+ public void setHasResults(boolean hasResults) {
+ this.hasResults = hasResults;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
index 1d3f6e0..a1d87ad 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
@@ -80,7 +80,7 @@ public class StoredOperationHandle implements Indexed {
identifier.setGuid(Hex.decodeHex(getGuid().toCharArray()));
identifier.setSecret(Hex.decodeHex(getSecret().toCharArray()));
} catch (DecoderException e) {
- throw new ServiceFormattedException("Wrong identifer of OperationHandle is stored in DB");
+ throw new ServiceFormattedException("E060 Wrong identifier of OperationHandle is stored in DB");
}
handle.setOperationId(identifier);
return handle;
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
index 106babd..a6446ff 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
@@ -91,6 +91,7 @@ public class ATSParser implements IATSParser {
HiveQueryId parsedJob = new HiveQueryId();
parsedJob.entity = (String) job.get("entity");
+ parsedJob.url = delegate.hiveQueryIdDirectUrl((String) job.get("entity"));
parsedJob.starttime = ((Long) job.get("starttime")) / MillisInSecond;
JSONObject primaryfilters = (JSONObject) job.get("primaryfilters");
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
index 3aa07d4..f9b19bf 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
@@ -21,6 +21,14 @@ package org.apache.ambari.view.hive.resources.jobs.atsJobs;
import org.json.simple.JSONObject;
public interface ATSRequestsDelegate {
+ String hiveQueryIdDirectUrl(String entity);
+
+ String hiveQueryIdOperationIdUrl(String operationId);
+
+ String tezDagDirectUrl(String entity);
+
+ String tezDagNameUrl(String name);
+
JSONObject hiveQueryIdList(String username);
JSONObject hiveQueryIdByOperationId(String operationId);
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
index 047bd63..6ac5ef8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
@@ -43,6 +43,26 @@ public class ATSRequestsDelegateImpl implements ATSRequestsDelegate {
}
@Override
+ public String hiveQueryIdDirectUrl(String entity) {
+ return atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID/" + entity;
+ }
+
+ @Override
+ public String hiveQueryIdOperationIdUrl(String operationId) {
+ return atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=operationid:" + operationId;
+ }
+
+ @Override
+ public String tezDagDirectUrl(String entity) {
+ return atsUrl + "/ws/v1/timeline/TEZ_DAG_ID/" + entity;
+ }
+
+ @Override
+ public String tezDagNameUrl(String name) {
+ return atsUrl + "/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:" + name;
+ }
+
+ @Override
public JSONObject hiveQueryIdList(String username) {
String hiveQueriesListUrl = atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=requestuser:" + username;
String response = readFromWithDefault(hiveQueriesListUrl, "{ \"entities\" : [ ] }");
@@ -51,14 +71,14 @@ public class ATSRequestsDelegateImpl implements ATSRequestsDelegate {
@Override
public JSONObject hiveQueryIdByOperationId(String operationId) {
- String hiveQueriesListUrl = atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=operationid:" + operationId;
+ String hiveQueriesListUrl = hiveQueryIdOperationIdUrl(operationId);
String response = readFromWithDefault(hiveQueriesListUrl, "{ \"entities\" : [ ] }");
return (JSONObject) JSONValue.parse(response);
}
@Override
public JSONObject tezDagByName(String name) {
- String tezDagUrl = atsUrl + "/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:" + name;
+ String tezDagUrl = tezDagNameUrl(name);
String response = readFromWithDefault(tezDagUrl, EMPTY_ENTITIES_JSON);
return (JSONObject) JSONValue.parse(response);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
index edb726b..af54dbb 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
@@ -23,6 +23,8 @@ import org.json.simple.JSONObject;
import java.util.List;
public class HiveQueryId {
+ public String url;
+
public String entity;
public String query;
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
index 65c36a7..dc4dfa8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
@@ -104,4 +104,12 @@ public interface Job extends Serializable,Indexed,PersonalResource {
String getSessionTag();
void setSessionTag(String sessionTag);
+
+ String getSqlState();
+
+ void setSqlState(String sqlState);
+
+ String getStatusMessage();
+
+ void setStatusMessage(String message);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
index 339e194..a25f7d4 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
@@ -35,6 +35,7 @@ public interface JobController {
Job getJobPOJO();
Cursor getResults() throws ItemNotFound;
+ boolean hasResults() throws ItemNotFound;
void afterCreation();
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
index c6c1c78..eb2e141 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
@@ -77,9 +77,9 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
try {
query = paginator.readPage(0); //warning - reading only 0 page restricts size of query to 1MB
} catch (IOException e) {
- throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+ throw new ServiceFormattedException("F030 Error when reading file " + job.getQueryFile(), e);
} catch (InterruptedException e) {
- throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+ throw new ServiceFormattedException("F030 Error when reading file " + job.getQueryFile(), e);
}
return query;
}
@@ -109,12 +109,19 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
}
private TSessionHandle getSession() {
- if (job.getSessionTag() != null) {
- return hiveConnection.getSessionByTag(getJob().getSessionTag());
- } else {
- String tag = hiveConnection.openSession();
- job.setSessionTag(tag);
+ try {
+ if (job.getSessionTag() != null)
+ return hiveConnection.getSessionByTag(getJob().getSessionTag());
+ } catch (HiveClientException ignore) {
+ LOG.debug("Stale sessionTag was provided, new session will be opened");
+ }
+
+ String tag = hiveConnection.openSession();
+ job.setSessionTag(tag);
+ try {
return hiveConnection.getSessionByTag(tag);
+ } catch (HiveClientException e) {
+ throw new HiveClientFormattedException(e);
}
}
@@ -136,8 +143,10 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
try {
OperationHandleController handle = opHandleControllerFactory.getHandleForJob(job);
- String status = handle.getOperationStatus();
- job.setStatus(status);
+ OperationHandleController.OperationStatus status = handle.getOperationStatus();
+ job.setStatus(status.status);
+ job.setStatusMessage(status.message);
+ job.setSqlState(status.sqlState);
LOG.debug("Status of job#" + job.getId() + " is " + job.getStatus());
} catch (NoOperationStatusSetException e) {
@@ -148,7 +157,7 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
job.setStatus(Job.JOB_STATE_UNKNOWN);
} catch (HiveClientException e) {
- throw new ServiceFormattedException("Could not fetch job status " + job.getId(), e);
+ throw new HiveClientFormattedException(e);
} catch (ItemNotFound itemNotFound) {
LOG.debug("No TOperationHandle for job#" + job.getId() + ", can't update status");
@@ -213,6 +222,12 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
}
@Override
+ public boolean hasResults() throws ItemNotFound {
+ OperationHandleController handle = opHandleControllerFactory.getHandleForJob(job);
+ return handle.hasResults();
+ }
+
+ @Override
public void afterCreation() {
setupStatusDirIfNotPresent();
setupQueryFileIfNotPresent();
@@ -311,9 +326,9 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
}
} catch (IOException e) {
- throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ throw new ServiceFormattedException("F040 Error when creating file " + jobQueryFilePath, e);
} catch (InterruptedException e) {
- throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ throw new ServiceFormattedException("F040 Error when creating file " + jobQueryFilePath, e);
}
job.setQueryFile(jobQueryFilePath);
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
index d0d74a2..dc3f624 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
@@ -20,6 +20,7 @@ package org.apache.ambari.view.hive.resources.jobs.viewJobs;
import org.apache.commons.beanutils.PropertyUtils;
+import java.beans.Transient;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@@ -32,11 +33,14 @@ public class JobImpl implements Job {
private String statusDir = null;
private Long dateSubmitted = 0L;
private Long duration = 0L;
- private String status = JOB_STATE_UNKNOWN;
private String forcedContent = null;
private String dataBase = null;
private String queryId = null;
+ private String status = JOB_STATE_UNKNOWN;
+ private String statusMessage = null;
+ private String sqlState = null;
+
private String applicationId;
private String dagId;
private String dagName;
@@ -148,11 +152,13 @@ public class JobImpl implements Job {
}
@Override
+ @Transient
public String getForcedContent() {
return forcedContent;
}
@Override
+ @Transient
public void setForcedContent(String forcedContent) {
this.forcedContent = forcedContent;
}
@@ -246,4 +252,24 @@ public class JobImpl implements Job {
public void setSessionTag(String sessionTag) {
this.sessionTag = sessionTag;
}
+
+ @Override
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ @Override
+ public void setStatusMessage(String statusMessage) {
+ this.statusMessage = statusMessage;
+ }
+
+ @Override
+ public String getSqlState() {
+ return sqlState;
+ }
+
+ @Override
+ public void setSqlState(String sqlState) {
+ this.sqlState = sqlState;
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
index 2dafcf4..77c37b5 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
@@ -31,7 +31,7 @@ import java.util.Map;
public class UDF implements Serializable, PersonalResource {
private String name;
private String classname;
- private Integer fileResource;
+ private String fileResource;
private String id;
private String owner;
@@ -77,11 +77,11 @@ public class UDF implements Serializable, PersonalResource {
this.classname = classname;
}
- public Integer getFileResource() {
+ public String getFileResource() {
return fileResource;
}
- public void setFileResource(Integer fileResource) {
+ public void setFileResource(String fileResource) {
this.fileResource = fileResource;
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
index e5e3593..844bc80 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
@@ -253,7 +253,7 @@ public class HdfsApi {
}
});
if (!result) {
- throw new ServiceFormattedException("Can't copy source file from " + src + " to " + dest);
+ throw new ServiceFormattedException("F050 Can't copy source file from " + src + " to " + dest);
}
}
@@ -341,11 +341,11 @@ public class HdfsApi {
api = new HdfsApi(defaultFS, getHdfsUsername(context), getHdfsAuthParams(context));
LOG.info("HdfsApi connected OK");
} catch (IOException e) {
- String message = "HdfsApi IO error: " + e.getMessage();
+ String message = "F060 Couldn't open connection to HDFS";
LOG.error(message);
throw new ServiceFormattedException(message, e);
} catch (InterruptedException e) {
- String message = "HdfsApi Interrupted error: " + e.getMessage();
+ String message = "F060 Couldn't open connection to HDFS";
LOG.error(message);
throw new ServiceFormattedException(message, e);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
index 3120958..7386ee4 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
@@ -43,9 +43,9 @@ public class HdfsUtil {
stream.close();
}
} catch (IOException e) {
- throw new ServiceFormattedException("Could not write file " + filePath, e);
+ throw new ServiceFormattedException("F070 Could not write file " + filePath, e);
} catch (InterruptedException e) {
- throw new ServiceFormattedException("Could not write file " + filePath, e);
+ throw new ServiceFormattedException("F070 Could not write file " + filePath, e);
}
}
@@ -74,9 +74,9 @@ public class HdfsUtil {
triesCount += 1;
} while (!isUnallocatedFilenameFound);
} catch (IOException e) {
- throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ throw new ServiceFormattedException("F080 Error in creation " + fullPathAndFilename + "...", e);
} catch (InterruptedException e) {
- throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ throw new ServiceFormattedException("F080 Error in creation " + fullPathAndFilename + "...", e);
}
return newFilePath;
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
new file mode 100644
index 0000000..a602d22
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
@@ -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.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+public class HiveClientFormattedException extends ServiceFormattedException {
+
+ public HiveClientFormattedException(Throwable exception) {
+ super(exception.getMessage(), exception);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc b/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
new file mode 100644
index 0000000..959e169
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
@@ -0,0 +1,4 @@
+{
+ "directory": "bower_components",
+ "analytics": false
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig b/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
new file mode 100644
index 0000000..47c5438
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
@@ -0,0 +1,34 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.js]
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+insert_final_newline = false
+indent_style = space
+indent_size = 2
+
+[*.css]
+indent_style = space
+indent_size = 2
+
+[*.html]
+indent_style = space
+indent_size = 2
+
+[*.{diff,md}]
+trim_trailing_whitespace = false
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli b/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
new file mode 100644
index 0000000..3da2738
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+{
+ /**
+ Ember CLI sends analytics information by default. The data is completely
+ anonymous, but there are times when you might want to disable this behavior.
+
+ Setting `disableAnalytics` to true will prevent any data from being sent.
+ */
+ "disableAnalytics": true
+}
[2/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain,
Error handling and bugfixes (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
new file mode 100644
index 0000000..10a0471
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
@@ -0,0 +1,27 @@
+/**
+ * @license
+ * Copyright (c) 2012-2014 Chris Pettitt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(global){global.dagre=require("./index");global.dagre.graphlib=require("graphlib")}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./index":2,graphlib:29}],2:[function(require,module,exports){module.exports={layout:require("./lib/layout"),debug:require("./lib/debug"),util:{time:require("./lib/util").time,notime:require("./lib/util").notime},version:require("./lib/version")}},{"./lib/debug":6,"./lib/layout":8,"./lib/util":27,"./lib/v
ersion":28}],3:[function(require,module,exports){"use strict";var _=require("lodash"),greedyFAS=require("./greedy-fas");module.exports={run:run,undo:undo};function run(g){var fas=g.graph().acyclicer==="greedy"?greedyFAS(g,weightFn(g)):dfsFAS(g);_.each(fas,function(e){var label=g.edge(e);g.removeEdge(e);label.forwardName=e.name;label.reversed=true;g.setEdge(e.w,e.v,label,_.uniqueId("rev"))});function weightFn(g){return function(e){return g.edge(e).weight}}}function dfsFAS(g){var fas=[],stack={},visited={};function dfs(v){if(_.has(visited,v)){return}visited[v]=true;stack[v]=true;_.each(g.outEdges(v),function(e){if(_.has(stack,e.w)){fas.push(e)}else{dfs(e.w)}});delete stack[v]}_.each(g.nodes(),dfs);return fas}function undo(g){_.each(g.edges(),function(e){var label=g.edge(e);if(label.reversed){g.removeEdge(e);var forwardName=label.forwardName;delete label.reversed;delete label.forwardName;g.setEdge(e.w,e.v,label,forwardName)}})}},{"./greedy-fas":7,lodash:48}],4:[function(require,module,
exports){var _=require("lodash"),util=require("./util");module.exports=addBorderSegments;function addBorderSegments(g){function dfs(v){var children=g.children(v),node=g.node(v);if(children.length){_.each(children,dfs)}if(_.has(node,"minRank")){node.borderLeft=[];node.borderRight=[];for(var rank=node.minRank,maxRank=node.maxRank+1;rank<maxRank;++rank){addBorderNode(g,"borderLeft","_bl",v,node,rank);addBorderNode(g,"borderRight","_br",v,node,rank)}}}_.each(g.children(),dfs)}function addBorderNode(g,prop,prefix,sg,sgNode,rank){var label={width:0,height:0,rank:rank},prev=sgNode[prop][rank-1],curr=util.addDummyNode(g,"border",label,prefix);sgNode[prop][rank]=curr;g.setParent(curr,sg);if(prev){g.setEdge(prev,curr,{weight:1})}}},{"./util":27,lodash:48}],5:[function(require,module,exports){module.exports=List;function List(){var sentinel={};sentinel._next=sentinel._prev=sentinel;this._sentinel=sentinel}List.prototype.dequeue=function(){var sentinel=this._sentinel,entry=sentinel._prev;if(ent
ry!==sentinel){unlink(entry);return entry}};List.prototype.enqueue=function(entry){var sentinel=this._sentinel;if(entry._prev&&entry._next){unlink(entry)}entry._next=sentinel._next;sentinel._next._prev=entry;sentinel._next=entry;entry._prev=sentinel};List.prototype.toString=function(){var strs=[],sentinel=this._sentinel,curr=sentinel._prev;while(curr!==sentinel){strs.push(JSON.stringify(curr,filterOutLinks));curr=curr._prev}return"["+strs.join(", ")+"]"};function unlink(entry){entry._prev._next=entry._next;entry._next._prev=entry._prev;delete entry._next;delete entry._prev}function filterOutLinks(k,v){if(k!=="_next"&&k!=="_prev"){return v}}},{}],6:[function(require,module,exports){var _=require("lodash"),util=require("./util"),Graph=require("graphlib").Graph;module.exports={debugOrdering:debugOrdering};function debugOrdering(g){var layerMatrix=util.buildLayerMatrix(g);var h=new Graph({compound:true,multigraph:true}).setGraph({});_.each(g.nodes(),function(v){h.setNode(v,{label:v});h.
setParent(v,"layer"+g.node(v).rank)});_.each(g.edges(),function(e){h.setEdge(e.v,e.w,{},e.name)});_.each(layerMatrix,function(layer,i){var layerV="layer"+i;h.setNode(layerV,{rank:"same"});_.reduce(layer,function(u,v){h.setEdge(u,v,{style:"invis"});return v})});return h}},{"./util":27,graphlib:29,lodash:48}],7:[function(require,module,exports){var _=require("lodash"),Graph=require("graphlib").Graph,List=require("./data/list");module.exports=greedyFAS;var DEFAULT_WEIGHT_FN=_.constant(1);function greedyFAS(g,weightFn){if(g.nodeCount()<=1){return[]}var state=buildState(g,weightFn||DEFAULT_WEIGHT_FN);var results=doGreedyFAS(state.graph,state.buckets,state.zeroIdx);return _.flatten(_.map(results,function(e){return g.outEdges(e.v,e.w)}),true)}function doGreedyFAS(g,buckets,zeroIdx){var results=[],sources=buckets[buckets.length-1],sinks=buckets[0];var entry;while(g.nodeCount()){while(entry=sinks.dequeue()){removeNode(g,buckets,zeroIdx,entry)}while(entry=sources.dequeue()){removeNode(g,bucke
ts,zeroIdx,entry)}if(g.nodeCount()){for(var i=buckets.length-2;i>0;--i){entry=buckets[i].dequeue();if(entry){results=results.concat(removeNode(g,buckets,zeroIdx,entry,true));break}}}}return results}function removeNode(g,buckets,zeroIdx,entry,collectPredecessors){var results=collectPredecessors?[]:undefined;_.each(g.inEdges(entry.v),function(edge){var weight=g.edge(edge),uEntry=g.node(edge.v);if(collectPredecessors){results.push({v:edge.v,w:edge.w})}uEntry.out-=weight;assignBucket(buckets,zeroIdx,uEntry)});_.each(g.outEdges(entry.v),function(edge){var weight=g.edge(edge),w=edge.w,wEntry=g.node(w);wEntry.in-=weight;assignBucket(buckets,zeroIdx,wEntry)});g.removeNode(entry.v);return results}function buildState(g,weightFn){var fasGraph=new Graph,maxIn=0,maxOut=0;_.each(g.nodes(),function(v){fasGraph.setNode(v,{v:v,"in":0,out:0})});_.each(g.edges(),function(e){var prevWeight=fasGraph.edge(e.v,e.w)||0,weight=weightFn(e),edgeWeight=prevWeight+weight;fasGraph.setEdge(e.v,e.w,edgeWeight);max
Out=Math.max(maxOut,fasGraph.node(e.v).out+=weight);maxIn=Math.max(maxIn,fasGraph.node(e.w).in+=weight)});var buckets=_.range(maxOut+maxIn+3).map(function(){return new List});var zeroIdx=maxIn+1;_.each(fasGraph.nodes(),function(v){assignBucket(buckets,zeroIdx,fasGraph.node(v))});return{graph:fasGraph,buckets:buckets,zeroIdx:zeroIdx}}function assignBucket(buckets,zeroIdx,entry){if(!entry.out){buckets[0].enqueue(entry)}else if(!entry.in){buckets[buckets.length-1].enqueue(entry)}else{buckets[entry.out-entry.in+zeroIdx].enqueue(entry)}}},{"./data/list":5,graphlib:29,lodash:48}],8:[function(require,module,exports){"use strict";var _=require("lodash"),acyclic=require("./acyclic"),normalize=require("./normalize"),rank=require("./rank"),normalizeRanks=require("./util").normalizeRanks,parentDummyChains=require("./parent-dummy-chains"),removeEmptyRanks=require("./util").removeEmptyRanks,nestingGraph=require("./nesting-graph"),addBorderSegments=require("./add-border-segments"),order=require(".
/order"),position=require("./position"),util=require("./util"),Graph=require("graphlib").Graph;module.exports=layout;function layout(g,opts){var time=opts&&opts.debugTiming?util.time:util.notime;time("layout",function(){var layoutGraph=time(" buildLayoutGraph",function(){return buildLayoutGraph(g)});time(" runLayout",function(){runLayout(layoutGraph,time)});time(" updateInputGraph",function(){updateInputGraph(g,layoutGraph)})})}function runLayout(g,time){time(" makeSpaceForEdgeLabels",function(){makeSpaceForEdgeLabels(g)});time(" removeSelfEdges",function(){removeSelfEdges(g)});time(" acyclic",function(){acyclic.run(g)});time(" nestingGraph.run",function(){nestingGraph.run(g)});time(" rank",function(){rank(util.asNonCompoundGraph(g))});time(" injectEdgeLabelProxies",function(){injectEdgeLabelProxies(g)});time(" removeEmptyRanks",function(){removeEmptyRanks(g)});time(" nestingGraph.cleanup",function(){nestingGraph.cleanup(g)});time(" normalizeRanks",func
tion(){normalizeRanks(g)});time(" assignRankMinMax",function(){assignRankMinMax(g)});time(" removeEdgeLabelProxies",function(){removeEdgeLabelProxies(g)});time(" normalize.run",function(){normalize.run(g)});time(" parentDummyChains",function(){parentDummyChains(g)});time(" addBorderSegments",function(){addBorderSegments(g)});time(" order",function(){order(g)});time(" insertSelfEdges",function(){insertSelfEdges(g)});time(" position",function(){position(g)});time(" positionSelfEdges",function(){positionSelfEdges(g)});time(" setGraphDimensions",function(){setGraphDimensions(g)});time(" removeBorderNodes",function(){removeBorderNodes(g)});time(" normalize.undo",function(){normalize.undo(g)});time(" assignNodeIntersects",function(){assignNodeIntersects(g)});time(" reversePoints",function(){reversePointsForReversedEdges(g)});time(" acyclic.undo",function(){acyclic.undo(g)})}function updateInputGraph(inputGraph,layoutGraph){_.each(inputGraph.nod
es(),function(v){var inputLabel=inputGraph.node(v),layoutLabel=layoutGraph.node(v);if(inputLabel){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y;if(layoutGraph.children(v).length){inputLabel.width=layoutLabel.width;inputLabel.height=layoutLabel.height}}});_.each(inputGraph.edges(),function(e){var inputLabel=inputGraph.edge(e),layoutLabel=layoutGraph.edge(e);inputLabel.points=layoutLabel.points;if(_.has(layoutLabel,"x")){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y}});inputGraph.graph().width=layoutGraph.graph().width;inputGraph.graph().height=layoutGraph.graph().height}var graphNumAttrs=["nodesep","edgesep","ranksep","marginx","marginy"],graphDefaults={ranksep:50,edgesep:10,nodesep:50},graphAttrs=["acyclicer","ranker","rankdir","align"],nodeNumAttrs=["width","height"],nodeDefaults={width:0,height:0},edgeNumAttrs=["minlen","weight","width","height"],edgeDefaults={minlen:1,weight:1,width:0,height:0};function buildLayoutGraph(inputGraph){var g=new Graph({multigraph:true
,compound:true});g.setGraph(_.merge({},graphDefaults,selectNumberAttrs(inputGraph.graph(),graphNumAttrs),_.pick(inputGraph.graph(),graphAttrs)));_.each(inputGraph.nodes(),function(v){var label=inputGraph.node(v);g.setNode(v,_.defaults(selectNumberAttrs(label,nodeNumAttrs),nodeDefaults));g.setParent(v,inputGraph.parent(v))});_.each(inputGraph.edges(),function(e){var label=inputGraph.edge(e);g.setEdge(e,_.defaults(selectNumberAttrs(label,edgeNumAttrs),edgeDefaults))});return g}function makeSpaceForEdgeLabels(g){var graphLabel=g.graph();graphLabel.ranksep/=2;_.each(g.edges(),function(e){g.edge(e).minlen*=2})}function injectEdgeLabelProxies(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.width&&edge.height){var v=g.node(e.v),w=g.node(e.w),label={rank:(w.rank-v.rank)/2+v.rank,e:e};util.addDummyNode(g,"edge-proxy",label,"_ep")}})}function assignRankMinMax(g){var maxRank=0;_.each(g.nodes(),function(v){var node=g.node(v);if(node.borderTop){node.minRank=g.node(node.borderTop).rank
;node.maxRank=g.node(node.borderBottom).rank;maxRank=_.max(maxRank,node.maxRank)}});g.graph().maxRank=maxRank}function removeEdgeLabelProxies(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="edge-proxy"){g.edge(node.e).labelRank=node.rank;g.removeNode(v)}})}function setGraphDimensions(g){var width=0,height=0,graph=g.graph();_.each(g.nodes(),function(v){var node=g.node(v);width=Math.max(width,node.x+node.width/2);height=Math.max(height,node.y+node.height/2)});graph.width=width;graph.height=height}function assignNodeIntersects(g){_.each(g.edges(),function(e){var edge=g.edge(e),nodeV=g.node(e.v),nodeW=g.node(e.w),p1,p2;if(!edge.points){edge.points=[];p1=nodeW;p2=nodeV}else{p1=edge.points[0];p2=edge.points[edge.points.length-1]}edge.points.unshift(util.intersectRect(nodeV,p1));edge.points.push(util.intersectRect(nodeW,p2))})}function reversePointsForReversedEdges(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.reversed){edge.points.reverse()}})}function rem
oveBorderNodes(g){var rankdir=(g.graph().rankdir||"tb").toLowerCase();_.each(g.nodes(),function(v){if(g.children(v).length){var node=g.node(v),t=g.node(node.borderTop),b=g.node(node.borderBottom),l=g.node(_.last(node.borderLeft)),r=g.node(_.last(node.borderRight)),tmp;if(rankdir==="bt"||rankdir==="rl"){tmp=t;t=b;b=tmp}if(rankdir==="lr"||rankdir==="rl"){tmp=t;t=l;l=tmp;tmp=b;b=r;r=tmp}node.width=r.x-l.x;node.height=b.y-t.y;node.x=l.x+node.width/2;node.y=t.y+node.height/2}});_.each(g.nodes(),function(v){if(g.node(v).dummy==="border"){g.removeNode(v)}})}function removeSelfEdges(g){_.each(g.edges(),function(e){if(e.v===e.w){var node=g.node(e.v);if(!node.selfEdges){node.selfEdges=[]}node.selfEdges.push({e:e,label:g.edge(e)});g.removeEdge(e)}})}function insertSelfEdges(g){var layers=util.buildLayerMatrix(g);_.each(layers,function(layer){var orderShift=0;_.each(layer,function(v,i){var node=g.node(v);node.order=i+orderShift;_.each(node.selfEdges,function(selfEdge){util.addDummyNode(g,"selfe
dge",{width:selfEdge.label.width,height:selfEdge.label.height,rank:node.rank,order:i+ ++orderShift,e:selfEdge.e,label:selfEdge.label},"_se")});delete node.selfEdges})})}function positionSelfEdges(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="selfedge"){var selfNode=g.node(node.e.v),x=selfNode.x+selfNode.width/2,y=selfNode.y,dx=node.x-x,dy=selfNode.height/2;g.setEdge(node.e,node.label);g.removeNode(v);node.label.points=[{x:x+2*dx/3,y:y-dy},{x:x+5*dx/6,y:y-dy},{x:x+dx,y:y},{x:x+5*dx/6,y:y+dy},{x:x+2*dx/3,y:y+dy}];node.label.x=node.x;node.label.y=node.y}})}function selectNumberAttrs(obj,attrs){return _.mapValues(_.pick(obj,attrs),Number)}},{"./acyclic":3,"./add-border-segments":4,"./nesting-graph":9,"./normalize":10,"./order":15,"./parent-dummy-chains":20,"./position":22,"./rank":24,"./util":27,graphlib:29,lodash:48}],9:[function(require,module,exports){var _=require("lodash"),util=require("./util");module.exports={run:run,cleanup:cleanup};function run(g){var root
=util.addDummyNode(g,"root",{},"_root"),depths=treeDepths(g),height=_.max(depths)-1,nodeSep=2*height+1;g.graph().nestingRoot=root;_.each(g.edges(),function(e){g.edge(e).minlen*=nodeSep});var weight=sumWeights(g)+1;_.each(g.children(),function(child){dfs(g,root,nodeSep,weight,height,depths,child)});g.graph().nodeRankFactor=nodeSep}function dfs(g,root,nodeSep,weight,height,depths,v){var children=g.children(v);if(!children.length){if(v!==root){g.setEdge(root,v,{weight:0,minlen:nodeSep})}return}var top=util.addBorderNode(g,"_bt"),bottom=util.addBorderNode(g,"_bb"),label=g.node(v);g.setParent(top,v);label.borderTop=top;g.setParent(bottom,v);label.borderBottom=bottom;_.each(children,function(child){dfs(g,root,nodeSep,weight,height,depths,child);var childNode=g.node(child),childTop=childNode.borderTop?childNode.borderTop:child,childBottom=childNode.borderBottom?childNode.borderBottom:child,thisWeight=childNode.borderTop?weight:2*weight,minlen=childTop!==childBottom?1:height-depths[v]+1;g.s
etEdge(top,childTop,{weight:thisWeight,minlen:minlen,nestingEdge:true});g.setEdge(childBottom,bottom,{weight:thisWeight,minlen:minlen,nestingEdge:true})});if(!g.parent(v)){g.setEdge(root,top,{weight:0,minlen:height+depths[v]})}}function treeDepths(g){var depths={};function dfs(v,depth){var children=g.children(v);if(children&&children.length){_.each(children,function(child){dfs(child,depth+1)})}depths[v]=depth}_.each(g.children(),function(v){dfs(v,1)});return depths}function sumWeights(g){return _.reduce(g.edges(),function(acc,e){return acc+g.edge(e).weight},0)}function cleanup(g){var graphLabel=g.graph();g.removeNode(graphLabel.nestingRoot);delete graphLabel.nestingRoot;_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.nestingEdge){g.removeEdge(e)}})}},{"./util":27,lodash:48}],10:[function(require,module,exports){"use strict";var _=require("lodash"),util=require("./util");module.exports={run:run,undo:undo};function run(g){g.graph().dummyChains=[];_.each(g.edges(),function(edge
){normalizeEdge(g,edge)})}function normalizeEdge(g,e){var v=e.v,vRank=g.node(v).rank,w=e.w,wRank=g.node(w).rank,name=e.name,edgeLabel=g.edge(e),labelRank=edgeLabel.labelRank;if(wRank===vRank+1)return;g.removeEdge(e);var dummy,attrs,i;for(i=0,++vRank;vRank<wRank;++i,++vRank){edgeLabel.points=[];attrs={width:0,height:0,edgeLabel:edgeLabel,edgeObj:e,rank:vRank};dummy=util.addDummyNode(g,"edge",attrs,"_d");if(vRank===labelRank){attrs.width=edgeLabel.width;attrs.height=edgeLabel.height;attrs.dummy="edge-label"}g.setEdge(v,dummy,{weight:edgeLabel.weight},name);if(i===0){g.graph().dummyChains.push(dummy)}v=dummy}g.setEdge(v,w,{weight:edgeLabel.weight},name)}function undo(g){_.each(g.graph().dummyChains,function(v){var node=g.node(v),origLabel=node.edgeLabel,w;g.setEdge(node.edgeObj,origLabel);while(node.dummy){w=g.successors(v)[0];g.removeNode(v);origLabel.points.push({x:node.x,y:node.y});if(node.dummy==="edge-label"){origLabel.x=node.x;origLabel.y=node.y}v=w;node=g.node(v)}})}},{"./util":
27,lodash:48}],11:[function(require,module,exports){var _=require("lodash");module.exports=addSubgraphConstraints;function addSubgraphConstraints(g,cg,vs){var prev={},rootPrev;_.each(vs,function(v){var child=g.parent(v),parent,prevChild;while(child){parent=g.parent(child);if(parent){prevChild=prev[parent];prev[parent]=child}else{prevChild=rootPrev;rootPrev=child}if(prevChild&&prevChild!==child){cg.setEdge(prevChild,child);return}child=parent}})}},{lodash:48}],12:[function(require,module,exports){var _=require("lodash");module.exports=barycenter;function barycenter(g,movable){return _.map(movable,function(v){var inV=g.inEdges(v);if(!inV.length){return{v:v}}else{var result=_.reduce(inV,function(acc,e){var edge=g.edge(e),nodeU=g.node(e.v);return{sum:acc.sum+edge.weight*nodeU.order,weight:acc.weight+edge.weight}},{sum:0,weight:0});return{v:v,barycenter:result.sum/result.weight,weight:result.weight}}})}},{lodash:48}],13:[function(require,module,exports){var _=require("lodash"),Graph=requ
ire("graphlib").Graph;module.exports=buildLayerGraph;function buildLayerGraph(g,rank,relationship){var root=createRootNode(g),result=new Graph({compound:true}).setGraph({root:root}).setDefaultNodeLabel(function(v){return g.node(v)});_.each(g.nodes(),function(v){var node=g.node(v),parent=g.parent(v);if(node.rank===rank||node.minRank<=rank&&rank<=node.maxRank){result.setNode(v);result.setParent(v,parent||root);_.each(g[relationship](v),function(e){var u=e.v===v?e.w:e.v,edge=result.edge(u,v),weight=!_.isUndefined(edge)?edge.weight:0;result.setEdge(u,v,{weight:g.edge(e).weight+weight})});if(_.has(node,"minRank")){result.setNode(v,{borderLeft:node.borderLeft[rank],borderRight:node.borderRight[rank]})}}});return result}function createRootNode(g){var v;while(g.hasNode(v=_.uniqueId("_root")));return v}},{graphlib:29,lodash:48}],14:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=crossCount;function crossCount(g,layering){var cc=0;for(var i=1;i<layering.l
ength;++i){cc+=twoLayerCrossCount(g,layering[i-1],layering[i])}return cc}function twoLayerCrossCount(g,northLayer,southLayer){var southPos=_.zipObject(southLayer,_.map(southLayer,function(v,i){return i}));var southEntries=_.flatten(_.map(northLayer,function(v){return _.chain(g.outEdges(v)).map(function(e){return{pos:southPos[e.w],weight:g.edge(e).weight}}).sortBy("pos").value()}),true);var firstIndex=1;while(firstIndex<southLayer.length)firstIndex<<=1;var treeSize=2*firstIndex-1;firstIndex-=1;var tree=_.map(new Array(treeSize),function(){return 0});var cc=0;_.each(southEntries.forEach(function(entry){var index=entry.pos+firstIndex;tree[index]+=entry.weight;var weightSum=0;while(index>0){if(index%2){weightSum+=tree[index+1]}index=index-1>>1;tree[index]+=entry.weight}cc+=entry.weight*weightSum}));return cc}},{lodash:48}],15:[function(require,module,exports){"use strict";var _=require("lodash"),initOrder=require("./init-order"),crossCount=require("./cross-count"),sortSubgraph=require("
./sort-subgraph"),buildLayerGraph=require("./build-layer-graph"),addSubgraphConstraints=require("./add-subgraph-constraints"),Graph=require("graphlib").Graph,util=require("../util");module.exports=order;function order(g){var maxRank=util.maxRank(g),downLayerGraphs=buildLayerGraphs(g,_.range(1,maxRank+1),"inEdges"),upLayerGraphs=buildLayerGraphs(g,_.range(maxRank-1,-1,-1),"outEdges");var layering=initOrder(g);assignOrder(g,layering);var bestCC=Number.POSITIVE_INFINITY,best;for(var i=0,lastBest=0;lastBest<4;++i,++lastBest){sweepLayerGraphs(i%2?downLayerGraphs:upLayerGraphs,i%4>=2);layering=util.buildLayerMatrix(g);var cc=crossCount(g,layering);if(cc<bestCC){lastBest=0;best=_.cloneDeep(layering);bestCC=cc}}assignOrder(g,best)}function buildLayerGraphs(g,ranks,relationship){return _.map(ranks,function(rank){return buildLayerGraph(g,rank,relationship)})}function sweepLayerGraphs(layerGraphs,biasRight){var cg=new Graph;_.each(layerGraphs,function(lg){var root=lg.graph().root;var sorted=so
rtSubgraph(lg,root,cg,biasRight);_.each(sorted.vs,function(v,i){lg.node(v).order=i});addSubgraphConstraints(lg,cg,sorted.vs)})}function assignOrder(g,layering){_.each(layering,function(layer){_.each(layer,function(v,i){g.node(v).order=i})})}},{"../util":27,"./add-subgraph-constraints":11,"./build-layer-graph":13,"./cross-count":14,"./init-order":16,"./sort-subgraph":18,graphlib:29,lodash:48}],16:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=initOrder;function initOrder(g){var visited={},simpleNodes=_.filter(g.nodes(),function(v){return!g.children(v).length}),maxRank=_.max(_.map(simpleNodes,function(v){return g.node(v).rank})),layers=_.map(_.range(maxRank+1),function(){return[]});function dfs(v){if(_.has(visited,v))return;visited[v]=true;var node=g.node(v);layers[node.rank].push(v);_.each(g.successors(v),dfs)}var orderedVs=_.sortBy(simpleNodes,function(v){return g.node(v).rank});_.each(orderedVs,dfs);return layers}},{lodash:48}],17:[function(re
quire,module,exports){"use strict";var _=require("lodash");module.exports=resolveConflicts;function resolveConflicts(entries,cg){var mappedEntries={};_.each(entries,function(entry,i){var tmp=mappedEntries[entry.v]={indegree:0,"in":[],out:[],vs:[entry.v],i:i};if(!_.isUndefined(entry.barycenter)){tmp.barycenter=entry.barycenter;tmp.weight=entry.weight}});_.each(cg.edges(),function(e){var entryV=mappedEntries[e.v],entryW=mappedEntries[e.w];if(!_.isUndefined(entryV)&&!_.isUndefined(entryW)){entryW.indegree++;entryV.out.push(mappedEntries[e.w])}});var sourceSet=_.filter(mappedEntries,function(entry){return!entry.indegree});return doResolveConflicts(sourceSet)}function doResolveConflicts(sourceSet){var entries=[];function handleIn(vEntry){return function(uEntry){if(uEntry.merged){return}if(_.isUndefined(uEntry.barycenter)||_.isUndefined(vEntry.barycenter)||uEntry.barycenter>=vEntry.barycenter){mergeEntries(vEntry,uEntry)}}}function handleOut(vEntry){return function(wEntry){wEntry.in.push(
vEntry);if(--wEntry.indegree===0){sourceSet.push(wEntry)}}}while(sourceSet.length){var entry=sourceSet.pop();entries.push(entry);_.each(entry.in.reverse(),handleIn(entry));_.each(entry.out,handleOut(entry))}return _.chain(entries).filter(function(entry){return!entry.merged}).map(function(entry){return _.pick(entry,["vs","i","barycenter","weight"])}).value()}function mergeEntries(target,source){var sum=0,weight=0;if(target.weight){sum+=target.barycenter*target.weight;weight+=target.weight}if(source.weight){sum+=source.barycenter*source.weight;weight+=source.weight}target.vs=source.vs.concat(target.vs);target.barycenter=sum/weight;target.weight=weight;target.i=Math.min(source.i,target.i);source.merged=true}},{lodash:48}],18:[function(require,module,exports){var _=require("lodash"),barycenter=require("./barycenter"),resolveConflicts=require("./resolve-conflicts"),sort=require("./sort");module.exports=sortSubgraph;function sortSubgraph(g,v,cg,biasRight){var movable=g.children(v),node=g.
node(v),bl=node?node.borderLeft:undefined,br=node?node.borderRight:undefined,subgraphs={};if(bl){movable=_.filter(movable,function(w){return w!==bl&&w!==br})}var barycenters=barycenter(g,movable);_.each(barycenters,function(entry){if(g.children(entry.v).length){var subgraphResult=sortSubgraph(g,entry.v,cg,biasRight);subgraphs[entry.v]=subgraphResult;if(_.has(subgraphResult,"barycenter")){mergeBarycenters(entry,subgraphResult)}}});var entries=resolveConflicts(barycenters,cg);expandSubgraphs(entries,subgraphs);var result=sort(entries,biasRight);if(bl){result.vs=_.flatten([bl,result.vs,br],true);if(g.predecessors(bl).length){var blPred=g.node(g.predecessors(bl)[0]),brPred=g.node(g.predecessors(br)[0]);if(!_.has(result,"barycenter")){result.barycenter=0;result.weight=0}result.barycenter=(result.barycenter*result.weight+blPred.order+brPred.order)/(result.weight+2);result.weight+=2}}return result}function expandSubgraphs(entries,subgraphs){_.each(entries,function(entry){entry.vs=_.flatten
(entry.vs.map(function(v){if(subgraphs[v]){return subgraphs[v].vs}return v}),true)})}function mergeBarycenters(target,other){if(!_.isUndefined(target.barycenter)){target.barycenter=(target.barycenter*target.weight+other.barycenter*other.weight)/(target.weight+other.weight);target.weight+=other.weight}else{target.barycenter=other.barycenter;target.weight=other.weight}}},{"./barycenter":12,"./resolve-conflicts":17,"./sort":19,lodash:48}],19:[function(require,module,exports){var _=require("lodash"),util=require("../util");module.exports=sort;function sort(entries,biasRight){var parts=util.partition(entries,function(entry){return _.has(entry,"barycenter")});var sortable=parts.lhs,unsortable=_.sortBy(parts.rhs,function(entry){return-entry.i}),vs=[],sum=0,weight=0,vsIndex=0;sortable.sort(compareWithBias(!!biasRight));vsIndex=consumeUnsortable(vs,unsortable,vsIndex);_.each(sortable,function(entry){vsIndex+=entry.vs.length;vs.push(entry.vs);sum+=entry.barycenter*entry.weight;weight+=entry.w
eight;vsIndex=consumeUnsortable(vs,unsortable,vsIndex)});var result={vs:_.flatten(vs,true)};if(weight){result.barycenter=sum/weight;result.weight=weight}return result}function consumeUnsortable(vs,unsortable,index){var last;while(unsortable.length&&(last=_.last(unsortable)).i<=index){unsortable.pop();vs.push(last.vs);index++}return index}function compareWithBias(bias){return function(entryV,entryW){if(entryV.barycenter<entryW.barycenter){return-1}else if(entryV.barycenter>entryW.barycenter){return 1}return!bias?entryV.i-entryW.i:entryW.i-entryV.i}}},{"../util":27,lodash:48}],20:[function(require,module,exports){var _=require("lodash");module.exports=parentDummyChains;function parentDummyChains(g){var postorderNums=postorder(g);_.each(g.graph().dummyChains,function(v){var node=g.node(v),edgeObj=node.edgeObj,pathData=findPath(g,postorderNums,edgeObj.v,edgeObj.w),path=pathData.path,lca=pathData.lca,pathIdx=0,pathV=path[pathIdx],ascending=true;while(v!==edgeObj.w){node=g.node(v);if(asce
nding){while((pathV=path[pathIdx])!==lca&&g.node(pathV).maxRank<node.rank){pathIdx++}if(pathV===lca){ascending=false}}if(!ascending){while(pathIdx<path.length-1&&g.node(pathV=path[pathIdx+1]).minRank<=node.rank){pathIdx++}pathV=path[pathIdx]}g.setParent(v,pathV);v=g.successors(v)[0]}})}function findPath(g,postorderNums,v,w){var vPath=[],wPath=[],low=Math.min(postorderNums[v].low,postorderNums[w].low),lim=Math.max(postorderNums[v].lim,postorderNums[w].lim),parent,lca;parent=v;do{parent=g.parent(parent);vPath.push(parent)}while(parent&&(postorderNums[parent].low>low||lim>postorderNums[parent].lim));lca=parent;parent=w;while((parent=g.parent(parent))!==lca){wPath.push(parent)}return{path:vPath.concat(wPath.reverse()),lca:lca}}function postorder(g){var result={},lim=0;function dfs(v){var low=lim;_.each(g.children(v),dfs);result[v]={low:low,lim:lim++}}_.each(g.children(),dfs);return result}},{lodash:48}],21:[function(require,module,exports){"use strict";var _=require("lodash"),util=requi
re("../util");module.exports={positionX:positionX,findType1Conflicts:findType1Conflicts,findType2Conflicts:findType2Conflicts,addConflict:addConflict,hasConflict:hasConflict,verticalAlignment:verticalAlignment,horizontalCompaction:horizontalCompaction,alignCoordinates:alignCoordinates,findSmallestWidthAlignment:findSmallestWidthAlignment,balance:balance};function findType1Conflicts(g,layering){var conflicts={};function visitLayer(prevLayer,layer){var k0=0,scanPos=0,prevLayerLength=prevLayer.length,lastNode=_.last(layer);_.each(layer,function(v,i){var w=findOtherInnerSegmentNode(g,v),k1=w?g.node(w).order:prevLayerLength;if(w||v===lastNode){_.each(layer.slice(scanPos,i+1),function(scanNode){_.each(g.predecessors(scanNode),function(u){var uLabel=g.node(u),uPos=uLabel.order;if((uPos<k0||k1<uPos)&&!(uLabel.dummy&&g.node(scanNode).dummy)){addConflict(conflicts,u,scanNode)}})});scanPos=i+1;k0=k1}});return layer}_.reduce(layering,visitLayer);return conflicts}function findType2Conflicts(g,la
yering){var conflicts={};function scan(south,southPos,southEnd,prevNorthBorder,nextNorthBorder){var v;_.each(_.range(southPos,southEnd),function(i){v=south[i];if(g.node(v).dummy){_.each(g.predecessors(v),function(u){var uNode=g.node(u);if(uNode.dummy&&(uNode.order<prevNorthBorder||uNode.order>nextNorthBorder)){addConflict(conflicts,u,v)}})}})}function visitLayer(north,south){var prevNorthPos=-1,nextNorthPos,southPos=0;_.each(south,function(v,southLookahead){if(g.node(v).dummy==="border"){var predecessors=g.predecessors(v);if(predecessors.length){nextNorthPos=g.node(predecessors[0]).order;scan(south,southPos,southLookahead,prevNorthPos,nextNorthPos);southPos=southLookahead;prevNorthPos=nextNorthPos}}scan(south,southPos,south.length,nextNorthPos,north.length)});return south}_.reduce(layering,visitLayer);return conflicts}function findOtherInnerSegmentNode(g,v){if(g.node(v).dummy){return _.find(g.predecessors(v),function(u){return g.node(u).dummy})}}function addConflict(conflicts,v,w){i
f(v>w){var tmp=v;v=w;w=tmp}var conflictsV=conflicts[v];if(!conflictsV){conflicts[v]=conflictsV={}}conflictsV[w]=true}function hasConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}return _.has(conflicts[v],w)}function verticalAlignment(g,layering,conflicts,neighborFn){var root={},align={},pos={};_.each(layering,function(layer){_.each(layer,function(v,order){root[v]=v;align[v]=v;pos[v]=order})});_.each(layering,function(layer){var prevIdx=-1;_.each(layer,function(v){var ws=neighborFn(v);if(ws.length){ws=_.sortBy(ws,function(w){return pos[w]});var mp=(ws.length-1)/2;for(var i=Math.floor(mp),il=Math.ceil(mp);i<=il;++i){var w=ws[i];if(align[v]===v&&prevIdx<pos[w]&&!hasConflict(conflicts,v,w)){align[w]=v;align[v]=root[v]=root[w];prevIdx=pos[w]}}}})});return{root:root,align:align}}function horizontalCompaction(g,layering,root,align){var shift={},sink={},xs={},pred={},graphLabel=g.graph(),sepFn=sep(graphLabel.nodesep,graphLabel.edgesep);_.each(layering,function(layer){_.each(layer,functio
n(v,order){sink[v]=v;shift[v]=Number.POSITIVE_INFINITY;pred[v]=layer[order-1]})});_.each(g.nodes(),function(v){if(root[v]===v){placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,v)}});_.each(layering,function(layer){_.each(layer,function(v){xs[v]=xs[root[v]];if(v===root[v]&&shift[sink[root[v]]]<Number.POSITIVE_INFINITY){xs[v]+=shift[sink[root[v]]]}})});return xs}function placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,v){if(_.has(xs,v))return;xs[v]=0;var w=v,u;do{if(pred[w]){u=root[pred[w]];placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,u);if(sink[v]===v){sink[v]=sink[u]}var delta=sepFn(g,w,pred[w]);if(sink[v]!==sink[u]){shift[sink[u]]=Math.min(shift[sink[u]],xs[v]-xs[u]-delta)}else{xs[v]=Math.max(xs[v],xs[u]+delta)}}w=align[w]}while(w!==v)}function findSmallestWidthAlignment(g,xss){return _.min(xss,function(xs){var min=_.min(xs,function(x,v){return x-width(g,v)/2}),max=_.max(xs,function(x,v){return x+width(g,v)/2});return max-min})}function alignCoordina
tes(xss,alignTo){var alignToMin=_.min(alignTo),alignToMax=_.max(alignTo);_.each(["u","d"],function(vert){_.each(["l","r"],function(horiz){var alignment=vert+horiz,xs=xss[alignment],delta;if(xs===alignTo)return;delta=horiz==="l"?alignToMin-_.min(xs):alignToMax-_.max(xs);if(delta){xss[alignment]=_.mapValues(xs,function(x){return x+delta})}})})}function balance(xss,align){return _.mapValues(xss.ul,function(ignore,v){if(align){return xss[align.toLowerCase()][v]}else{var xs=_.sortBy(_.pluck(xss,v));return(xs[1]+xs[2])/2}})}function positionX(g){var layering=util.buildLayerMatrix(g),conflicts=_.merge(findType1Conflicts(g,layering),findType2Conflicts(g,layering));
+var xss={},adjustedLayering;_.each(["u","d"],function(vert){adjustedLayering=vert==="u"?layering:_.values(layering).reverse();_.each(["l","r"],function(horiz){if(horiz==="r"){adjustedLayering=_.map(adjustedLayering,function(inner){return _.values(inner).reverse()})}var neighborFn=vert==="u"?g.predecessors.bind(g):g.successors.bind(g);var align=verticalAlignment(g,adjustedLayering,conflicts,neighborFn);var xs=horizontalCompaction(g,adjustedLayering,align.root,align.align);if(horiz==="r"){xs=_.mapValues(xs,function(x){return-x})}xss[vert+horiz]=xs})});var smallestWidth=findSmallestWidthAlignment(g,xss);alignCoordinates(xss,smallestWidth);return balance(xss,g.graph().align)}function sep(nodeSep,edgeSep){return function(g,v,u){var vLabel=g.node(v),uLabel=g.node(u),sums=uLabel.width+(uLabel.dummy?edgeSep:nodeSep)+(vLabel.dummy?edgeSep:nodeSep)+vLabel.width;return sums/2}}function width(g,v){return g.node(v).width}},{"../util":27,lodash:48}],22:[function(require,module,exports){"use stric
t";var _=require("lodash"),util=require("../util"),positionX=require("./bk").positionX;module.exports=position;function position(g){g=util.asNonCompoundGraph(g);var rankDir=(g.graph().rankdir||"tb").toLowerCase();if(rankDir==="lr"||rankDir==="rl"){swapWidthHeight(g)}positionY(g);_.each(positionX(g),function(x,v){g.node(v).x=x});if(rankDir==="bt"||rankDir==="rl"){reverseY(g)}if(rankDir==="lr"||rankDir==="rl"){swapXY(g);swapWidthHeight(g)}translate(g)}function positionY(g){var layering=util.buildLayerMatrix(g),rankSep=g.graph().ranksep,prevY=0;_.each(layering,function(layer){var maxHeight=_.max(_.map(layer,function(v){return g.node(v).height}));_.each(layer,function(v){g.node(v).y=prevY+maxHeight/2});prevY+=maxHeight+rankSep})}function translate(g){var minX=Number.POSITIVE_INFINITY,maxX=0,minY=Number.POSITIVE_INFINITY,maxY=0,graphLabel=g.graph(),marginX=graphLabel.marginx||0,marginY=graphLabel.marginy||0;_.each(g.nodes(),function(v){var node=g.node(v),x=node.x,y=node.y,w=node.width,h=
node.height;minX=Math.min(minX,x-w/2);maxX=Math.max(maxX,x+w/2);minY=Math.min(minY,y-h/2);maxY=Math.max(maxY,y+h/2)});minX-=marginX;minY-=marginY;_.each(g.nodes(),function(v){var node=g.node(v);node.x-=minX;node.y-=minY});graphLabel.width=maxX-minX+marginX;graphLabel.height=maxY-minY+marginY}function reverseY(g){_.each(g.nodes(),function(v){var node=g.node(v);node.y=-node.y})}function swapWidthHeight(g){_.each(g.nodes(),function(v){var node=g.node(v),width=node.width,height=node.height;node.width=height;node.height=width})}function swapXY(g){_.each(g.nodes(),function(v){var node=g.node(v),x=node.x,y=node.y;node.x=y;node.y=x})}},{"../util":27,"./bk":21,lodash:48}],23:[function(require,module,exports){"use strict";var _=require("lodash"),Graph=require("graphlib").Graph,slack=require("./util").slack;module.exports=feasibleTree;function feasibleTree(g){var t=new Graph({directed:false});var start=g.nodes()[0],size=g.nodeCount();t.setNode(start,{});var edge,delta;while(tightTree(t,g)<size
){edge=findMinSlackEdge(t,g);delta=t.hasNode(edge.v)?slack(g,edge):-slack(g,edge);shiftRanks(t,g,delta)}return t}function tightTree(t,g){function dfs(v){_.each(g.nodeEdges(v),function(e){var edgeV=e.v,w=v===edgeV?e.w:edgeV;if(!t.hasNode(w)&&!slack(g,e)){t.setNode(w,{});t.setEdge(v,w,{});dfs(w)}})}_.each(t.nodes(),dfs);return t.nodeCount()}function findMinSlackEdge(t,g){return _.min(g.edges(),function(e){if(t.hasNode(e.v)!==t.hasNode(e.w)){return slack(g,e)}})}function shiftRanks(t,g,delta){_.each(t.nodes(),function(v){g.node(v).rank+=delta})}},{"./util":26,graphlib:29,lodash:48}],24:[function(require,module,exports){"use strict";var rankUtil=require("./util"),longestPath=rankUtil.longestPath,feasibleTree=require("./feasible-tree"),networkSimplex=require("./network-simplex");module.exports=rank;function rank(g){switch(g.graph().ranker){case"network-simplex":networkSimplexRanker(g);break;case"tight-tree":tightTreeRanker(g);break;case"longest-path":longestPathRanker(g);break;default:ne
tworkSimplexRanker(g)}}var longestPathRanker=longestPath;function tightTreeRanker(g){longestPath(g);feasibleTree(g)}function networkSimplexRanker(g){networkSimplex(g)}},{"./feasible-tree":23,"./network-simplex":25,"./util":26}],25:[function(require,module,exports){"use strict";var _=require("lodash"),feasibleTree=require("./feasible-tree"),slack=require("./util").slack,initRank=require("./util").longestPath,preorder=require("graphlib").alg.preorder,postorder=require("graphlib").alg.postorder,simplify=require("../util").simplify;module.exports=networkSimplex;networkSimplex.initLowLimValues=initLowLimValues;networkSimplex.initCutValues=initCutValues;networkSimplex.calcCutValue=calcCutValue;networkSimplex.leaveEdge=leaveEdge;networkSimplex.enterEdge=enterEdge;networkSimplex.exchangeEdges=exchangeEdges;function networkSimplex(g){g=simplify(g);initRank(g);var t=feasibleTree(g);initLowLimValues(t);initCutValues(t,g);var e,f;while(e=leaveEdge(t)){f=enterEdge(t,g,e);exchangeEdges(t,g,e,f)}}
function initCutValues(t,g){var vs=postorder(t,t.nodes());vs=vs.slice(0,vs.length-1);_.each(vs,function(v){assignCutValue(t,g,v)})}function assignCutValue(t,g,child){var childLab=t.node(child),parent=childLab.parent;t.edge(child,parent).cutvalue=calcCutValue(t,g,child)}function calcCutValue(t,g,child){var childLab=t.node(child),parent=childLab.parent,childIsTail=true,graphEdge=g.edge(child,parent),cutValue=0;if(!graphEdge){childIsTail=false;graphEdge=g.edge(parent,child)}cutValue=graphEdge.weight;_.each(g.nodeEdges(child),function(e){var isOutEdge=e.v===child,other=isOutEdge?e.w:e.v;if(other!==parent){var pointsToHead=isOutEdge===childIsTail,otherWeight=g.edge(e).weight;cutValue+=pointsToHead?otherWeight:-otherWeight;if(isTreeEdge(t,child,other)){var otherCutValue=t.edge(child,other).cutvalue;cutValue+=pointsToHead?-otherCutValue:otherCutValue}}});return cutValue}function initLowLimValues(tree,root){if(arguments.length<2){root=tree.nodes()[0]}dfsAssignLowLim(tree,{},1,root)}function
dfsAssignLowLim(tree,visited,nextLim,v,parent){var low=nextLim,label=tree.node(v);visited[v]=true;_.each(tree.neighbors(v),function(w){if(!_.has(visited,w)){nextLim=dfsAssignLowLim(tree,visited,nextLim,w,v)}});label.low=low;label.lim=nextLim++;if(parent){label.parent=parent}else{delete label.parent}return nextLim}function leaveEdge(tree){return _.find(tree.edges(),function(e){return tree.edge(e).cutvalue<0})}function enterEdge(t,g,edge){var v=edge.v,w=edge.w;if(!g.hasEdge(v,w)){v=edge.w;w=edge.v}var vLabel=t.node(v),wLabel=t.node(w),tailLabel=vLabel,flip=false;if(vLabel.lim>wLabel.lim){tailLabel=wLabel;flip=true}var candidates=_.filter(g.edges(),function(edge){return flip===isDescendant(t,t.node(edge.v),tailLabel)&&flip!==isDescendant(t,t.node(edge.w),tailLabel)});return _.min(candidates,function(edge){return slack(g,edge)})}function exchangeEdges(t,g,e,f){var v=e.v,w=e.w;t.removeEdge(v,w);t.setEdge(f.v,f.w,{});initLowLimValues(t);initCutValues(t,g);updateRanks(t,g)}function update
Ranks(t,g){var root=_.find(t.nodes(),function(v){return!g.node(v).parent}),vs=preorder(t,root);vs=vs.slice(1);_.each(vs,function(v){var parent=t.node(v).parent,edge=g.edge(v,parent),flipped=false;if(!edge){edge=g.edge(parent,v);flipped=true}g.node(v).rank=g.node(parent).rank+(flipped?edge.minlen:-edge.minlen)})}function isTreeEdge(tree,u,v){return tree.hasEdge(u,v)}function isDescendant(tree,vLabel,rootLabel){return rootLabel.low<=vLabel.lim&&vLabel.lim<=rootLabel.lim}},{"../util":27,"./feasible-tree":23,"./util":26,graphlib:29,lodash:48}],26:[function(require,module,exports){"use strict";var _=require("lodash");module.exports={longestPath:longestPath,slack:slack};function longestPath(g){var visited={};function dfs(v){var label=g.node(v);if(_.has(visited,v)){return label.rank}visited[v]=true;var rank=_.min(_.map(g.outEdges(v),function(e){return dfs(e.w)-g.edge(e).minlen}));if(rank===Number.POSITIVE_INFINITY){rank=0}return label.rank=rank}_.each(g.sources(),dfs)}function slack(g,e){r
eturn g.node(e.w).rank-g.node(e.v).rank-g.edge(e).minlen}},{lodash:48}],27:[function(require,module,exports){"use strict";var _=require("lodash"),Graph=require("graphlib").Graph;module.exports={addDummyNode:addDummyNode,simplify:simplify,asNonCompoundGraph:asNonCompoundGraph,successorWeights:successorWeights,predecessorWeights:predecessorWeights,intersectRect:intersectRect,buildLayerMatrix:buildLayerMatrix,normalizeRanks:normalizeRanks,removeEmptyRanks:removeEmptyRanks,addBorderNode:addBorderNode,maxRank:maxRank,partition:partition,time:time,notime:notime};function addDummyNode(g,type,attrs,name){var v;do{v=_.uniqueId(name)}while(g.hasNode(v));attrs.dummy=type;g.setNode(v,attrs);return v}function simplify(g){var simplified=(new Graph).setGraph(g.graph());_.each(g.nodes(),function(v){simplified.setNode(v,g.node(v))});_.each(g.edges(),function(e){var simpleLabel=simplified.edge(e.v,e.w)||{weight:0,minlen:1},label=g.edge(e);simplified.setEdge(e.v,e.w,{weight:simpleLabel.weight+label.we
ight,minlen:Math.max(simpleLabel.minlen,label.minlen)})});return simplified}function asNonCompoundGraph(g){var simplified=new Graph({multigraph:g.isMultigraph()}).setGraph(g.graph());_.each(g.nodes(),function(v){if(!g.children(v).length){simplified.setNode(v,g.node(v))}});_.each(g.edges(),function(e){simplified.setEdge(e,g.edge(e))});return simplified}function successorWeights(g){var weightMap=_.map(g.nodes(),function(v){var sucs={};_.each(g.outEdges(v),function(e){sucs[e.w]=(sucs[e.w]||0)+g.edge(e).weight});return sucs});return _.zipObject(g.nodes(),weightMap)}function predecessorWeights(g){var weightMap=_.map(g.nodes(),function(v){var preds={};_.each(g.inEdges(v),function(e){preds[e.v]=(preds[e.v]||0)+g.edge(e).weight});return preds});return _.zipObject(g.nodes(),weightMap)}function intersectRect(rect,point){var x=rect.x;var y=rect.y;var dx=point.x-x;var dy=point.y-y;var w=rect.width/2;var h=rect.height/2;if(!dx&&!dy){throw new Error("Not possible to find intersection inside of th
e rectangle")}var sx,sy;if(Math.abs(dy)*w>Math.abs(dx)*h){if(dy<0){h=-h}sx=h*dx/dy;sy=h}else{if(dx<0){w=-w}sx=w;sy=w*dy/dx}return{x:x+sx,y:y+sy}}function buildLayerMatrix(g){var layering=_.map(_.range(maxRank(g)+1),function(){return[]});_.each(g.nodes(),function(v){var node=g.node(v),rank=node.rank;if(!_.isUndefined(rank)){layering[rank][node.order]=v}});return layering}function normalizeRanks(g){var min=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));_.each(g.nodes(),function(v){var node=g.node(v);if(_.has(node,"rank")){node.rank-=min}})}function removeEmptyRanks(g){var offset=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));var layers=[];_.each(g.nodes(),function(v){var rank=g.node(v).rank-offset;if(!_.has(layers,rank)){layers[rank]=[]}layers[rank].push(v)});var delta=0,nodeRankFactor=g.graph().nodeRankFactor;_.each(layers,function(vs,i){if(_.isUndefined(vs)&&i%nodeRankFactor!==0){--delta}else if(delta){_.each(vs,function(v){g.node(v).rank+=delta})}})}function
addBorderNode(g,prefix,rank,order){var node={width:0,height:0};if(arguments.length>=4){node.rank=rank;node.order=order}return addDummyNode(g,"border",node,prefix)}function maxRank(g){return _.max(_.map(g.nodes(),function(v){var rank=g.node(v).rank;if(!_.isUndefined(rank)){return rank}}))}function partition(collection,fn){var result={lhs:[],rhs:[]};_.each(collection,function(value){if(fn(value)){result.lhs.push(value)}else{result.rhs.push(value)}});return result}function time(name,fn){var start=_.now();try{return fn()}finally{console.log(name+" time: "+(_.now()-start)+"ms")}}function notime(name,fn){return fn()}},{graphlib:29,lodash:48}],28:[function(require,module,exports){module.exports="0.5.1"},{}],29:[function(require,module,exports){var _=require("lodash");module.exports=_.clone(require("./lib"));module.exports.json=require("./lib/json");module.exports.alg=require("./lib/alg")},{"./lib":45,"./lib/alg":36,"./lib/json":46,lodash:48}],30:[function(require,module,exports){var _=req
uire("lodash");module.exports=components;function components(g){var visited={},cmpts=[],cmpt;function dfs(v){if(_.has(visited,v))return;visited[v]=true;cmpt.push(v);_.each(g.successors(v),dfs);_.each(g.predecessors(v),dfs)}_.each(g.nodes(),function(v){cmpt=[];dfs(v);if(cmpt.length){cmpts.push(cmpt)}});return cmpts}},{lodash:48}],31:[function(require,module,exports){var _=require("lodash");module.exports=dfs;function dfs(g,vs,order){if(!_.isArray(vs)){vs=[vs]}var acc=[],visited={};_.each(vs,function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,acc)});return acc}function doDfs(g,v,postorder,visited,acc){if(!_.has(visited,v)){visited[v]=true;if(!postorder){acc.push(v)}_.each(g.neighbors(v),function(w){doDfs(g,w,postorder,visited,acc)});if(postorder){acc.push(v)}}}},{lodash:48}],32:[function(require,module,exports){var dijkstra=require("./dijkstra"),_=require("lodash");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,e
dgeFunc){return _.transform(g.nodes(),function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc)},{})}},{"./dijkstra":33,lodash:48}],33:[function(require,module,exports){var _=require("lodash"),PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=_.constant(1);function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={},pq=new PriorityQueue,v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w,wEntry=results[w],weight=weightFn(edge),distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={d
istance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":43,lodash:48}],34:[function(require,module,exports){var _=require("lodash"),tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return _.filter(tarjan(g),function(cmpt){return cmpt.length>1})}},{"./tarjan":41,lodash:48}],35:[function(require,module,exports){var _=require("lodash");module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=_.constant(1);function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={},nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(
edge){var w=edge.v===v?edge.w:edge.v,d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{lodash:48}],36:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":30,"./dijkstra":33,"./dijkstra-all":32,"./find-cycles":34,"./floyd-warshall":35,"./is-acyclic":37,"./postorder":38,"./preorder":39,"./prim":40,"./
tarjan":41,"./topsort":42}],37:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":42}],38:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":31}],39:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":31}],40:[function(require,module,exports){var _=require("lodash"),Graph=require("../graph"),PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph,parents={},pq=new PriorityQueue,v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v,pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(
g.nodeCount()===0){return result}_.each(g.nodes(),function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)});pq.decrease(g.nodes()[0],0);var init=false;while(pq.size()>0){v=pq.removeMin();if(_.has(parents,v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":43,"../graph":44,lodash:48}],41:[function(require,module,exports){var _=require("lodash");module.exports=tarjan;function tarjan(g){var index=0,stack=[],visited={},results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!_.has(visited,w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[],w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w
);results.push(cmpt)}}g.nodes().forEach(function(v){if(!_.has(visited,v)){dfs(v)}});return results}},{lodash:48}],42:[function(require,module,exports){var _=require("lodash");module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={},stack={},results=[];function visit(node){if(_.has(stack,node)){throw new CycleException}if(!_.has(visited,node)){stack[node]=true;visited[node]=true;_.each(g.predecessors(node),visit);delete stack[node];results.push(node)}}_.each(g.sinks(),visit);if(_.size(visited)!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}},{lodash:48}],43:[function(require,module,exports){var _=require("lodash");module.exports=PriorityQueue;function PriorityQueue(){this._arr=[];this._keyIndices={}}PriorityQueue.prototype.size=function(){return this._arr.length};PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})};PriorityQueue.prototype.has=function(key){return _.has(this
._keyIndices,key)};PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}};PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key};PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!_.has(keyIndices,key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false};PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key};PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priori
ty=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i,r=l+1,largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this._swap(i,largest);this._heapify(largest)}}};PriorityQueue.prototype._decrease=function(index){var arr=this._arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this._swap(index,parent);index=parent}};PriorityQueue.prototype._swap=function(i,j){var arr=this._arr;var keyIndices=this._keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}},{lodash:48}],44:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=Graph;var DEFAULT_EDGE_NAME="\x00",GRAPH_NODE="\x00",EDGE_KEY_DELIM="";function Graph(opts){this._isDirected=_.ha
s(opts,"directed")?opts.directed:true;this._isMultigraph=_.has(opts,"multigraph")?opts.multigraph:false;this._isCompound=_.has(opts,"compound")?opts.compound:false;this._label=undefined;this._defaultNodeLabelFn=_.constant(undefined);this._defaultEdgeLabelFn=_.constant(undefined);this._nodes={};if(this._isCompound){this._parent={};this._children={};this._children[GRAPH_NODE]={}}this._in={};this._preds={};this._out={};this._sucs={};this._edgeObjs={};this._edgeLabels={}}Graph.prototype._nodeCount=0;Graph.prototype._edgeCount=0;Graph.prototype.isDirected=function(){return this._isDirected};Graph.prototype.isMultigraph=function(){return this._isMultigraph};Graph.prototype.isCompound=function(){return this._isCompound};Graph.prototype.setGraph=function(label){this._label=label;return this};Graph.prototype.graph=function(){return this._label};Graph.prototype.setDefaultNodeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._defaultNodeLabelFn=new
Default;return this};Graph.prototype.nodeCount=function(){return this._nodeCount};Graph.prototype.nodes=function(){return _.keys(this._nodes)};Graph.prototype.sources=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._in[v])},this)};Graph.prototype.sinks=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._out[v])},this)};Graph.prototype.setNodes=function(vs,value){var args=arguments;_.each(vs,function(v){if(args.length>1){this.setNode(v,value)}else{this.setNode(v)}},this);return this};Graph.prototype.setNode=function(v,value){if(_.has(this._nodes,v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this};Graph.prototype.node=function(v){return this._nodes[v]};Graph.protot
ype.hasNode=function(v){return _.has(this._nodes,v)};Graph.prototype.removeNode=function(v){var self=this;if(_.has(this._nodes,v)){var removeEdge=function(e){self.removeEdge(self._edgeObjs[e])};delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];_.each(this.children(v),function(child){this.setParent(child)},this);delete this._children[v]}_.each(_.keys(this._in[v]),removeEdge);delete this._in[v];delete this._preds[v];_.each(_.keys(this._out[v]),removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this};Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(_.isUndefined(parent)){parent=GRAPH_NODE}else{for(var ancestor=parent;!_.isUndefined(ancestor);ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromP
arentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]};Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}};Graph.prototype.children=function(v){if(_.isUndefined(v)){v=GRAPH_NODE}if(this._isCompound){var children=this._children[v];if(children){return _.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}};Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return _.keys(predsV)}};Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return _.keys(sucsV)}};Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){return _.union(preds,this.successors(v))}};Graph.prototype.setDefaultEdgeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._
defaultEdgeLabelFn=newDefault;return this};Graph.prototype.edgeCount=function(){return this._edgeCount};Graph.prototype.edges=function(){return _.values(this._edgeObjs)};Graph.prototype.setPath=function(vs,value){var self=this,args=arguments;_.reduce(vs,function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this};Graph.prototype.setEdge=function(v,w,value,name){var valueSpecified=arguments.length>2;if(_.isPlainObject(arguments[0])){v=arguments[0].v;w=arguments[0].w;name=arguments[0].name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}var e=edgeArgsToId(this._isDirected,v,w,name);if(_.has(this._edgeLabels,e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(!_.isUndefined(name)&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")}this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirecte
d,v,w,name);v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this};Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]};Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return _.has(this._edgeLabels,e)};Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name),edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._ed
geCount--}return this};Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=_.values(inV);if(!u){return edges}return _.filter(edges,function(edge){return edge.v===u})}};Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=_.values(outV);if(!w){return edges}return _.filter(edges,function(edge){return edge.w===w})}};Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(_.has(map,k)){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(_.isUndefined(name)?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj)
{return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{lodash:48}],45:[function(require,module,exports){module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":44,"./version":47}],46:[function(require,module,exports){var _=require("lodash"),Graph=require("./graph");module.exports={write:write,read:read};function write(g){var json={options:{directed:g.isDirected(),multigraph:g.isMultigraph(),compound:g.isCompound()},nodes:writeNodes(g),edges:writeEdges(g)};if(!_.isUndefined(g.graph())){json.value=_.clone(g.graph())}return json}function writeNodes(g){return _.map(g.nodes(),function(v){var nodeValue=g.node(v),parent=g.parent(v),node={v:v};if(!_.isUndefined(nodeValue)){node.value=nodeValue}if(!_.isUndefined(parent)){node.parent=parent}return node})}function writeEdges(g){return _.map(g.edges(),function(e){var edgeValue=g.edge(e),edge={v:e.v,w:e.w};if(!_.isUndefined(e.name)){edge.name=e.name}if(!_.isUndefined(edgeValue)){edge.value=edgeValue}retur
n edge})}function read(json){var g=new Graph(json.options).setGraph(json.value);_.each(json.nodes,function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});_.each(json.edges,function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":44,lodash:48}],47:[function(require,module,exports){module.exports="0.8.0"},{}],48:[function(require,module,exports){(function(global){(function(){var undefined;var arrayPool=[],objectPool=[];var idCounter=0;var keyPrefix=+new Date+"";var largeArraySize=75;var maxPoolSize=40;var whitespace=" \f "+"\n\r\u2028\u2029"+" ";var reEmptyStringLeading=/\b__p \+= '';/g,reEmptyStringMiddle=/\b(__p \+=) '' \+/g,reEmptyStringTrailing=/(__e\(.*?\)|\b__t\)) \+\n'';/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reFuncName=/^\s*function[ \n\r\t]+\w/;var reInterpolate=/<%=([\s\S]+?)%>/g;var reLeadingSpaces
AndZeros=RegExp("^["+whitespace+"]*0+(?=.$)");var reNoMatch=/($^)/;var reThis=/\bthis\b/;var reUnescapedString=/['\n\r\t\u2028\u2029\\]/g;var contextProps=["Array","Boolean","Date","Function","Math","Number","Object","RegExp","String","_","attachEvent","clearTimeout","isFinite","isNaN","parseInt","setTimeout"];var templateCounter=0;var argsClass="[object Arguments]",arrayClass="[object Array]",boolClass="[object Boolean]",dateClass="[object Date]",funcClass="[object Function]",numberClass="[object Number]",objectClass="[object Object]",regexpClass="[object RegExp]",stringClass="[object String]";var cloneableClasses={};cloneableClasses[funcClass]=false;cloneableClasses[argsClass]=cloneableClasses[arrayClass]=cloneableClasses[boolClass]=cloneableClasses[dateClass]=cloneableClasses[numberClass]=cloneableClasses[objectClass]=cloneableClasses[regexpClass]=cloneableClasses[stringClass]=true;var debounceOptions={leading:false,maxWait:0,trailing:false};var descriptor={configurable:false,enu
merable:false,value:null,writable:false};var objectTypes={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};var root=objectTypes[typeof window]&&window||this;var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var freeGlobal=objectTypes[typeof global]&&global;if(freeGlobal&&(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal)){root=freeGlobal}function baseIndexOf(array,value,fromIndex){var index=(fromIndex||0)-1,length=array?array.length:0;while(++index<length){if(array[index]===value){return index}}return-1}function cacheIndexOf(cache,value){var type=typeof value;cache=cache.cache;if(type=="boolean"||value==null){return cache[value]?0:-1}if(type!="number"&&ty
pe!="string"){type="object"}var key=type=="number"?value:keyPrefix+value;cache=(cache=cache[type])&&cache[key];return type=="object"?cache&&baseIndexOf(cache,value)>-1?0:-1:cache?0:-1
+}function cachePush(value){var cache=this.cache,type=typeof value;if(type=="boolean"||value==null){cache[value]=true}else{if(type!="number"&&type!="string"){type="object"}var key=type=="number"?value:keyPrefix+value,typeCache=cache[type]||(cache[type]={});if(type=="object"){(typeCache[key]||(typeCache[key]=[])).push(value)}else{typeCache[key]=true}}}function charAtCallback(value){return value.charCodeAt(0)}function compareAscending(a,b){var ac=a.criteria,bc=b.criteria,index=-1,length=ac.length;while(++index<length){var value=ac[index],other=bc[index];if(value!==other){if(value>other||typeof value=="undefined"){return 1}if(value<other||typeof other=="undefined"){return-1}}}return a.index-b.index}function createCache(array){var index=-1,length=array.length,first=array[0],mid=array[length/2|0],last=array[length-1];if(first&&typeof first=="object"&&mid&&typeof mid=="object"&&last&&typeof last=="object"){return false}var cache=getObject();cache["false"]=cache["null"]=cache["true"]=cache[
"undefined"]=false;var result=getObject();result.array=array;result.cache=cache;result.push=cachePush;while(++index<length){result.push(array[index])}return result}function escapeStringChar(match){return"\\"+stringEscapes[match]}function getArray(){return arrayPool.pop()||[]}function getObject(){return objectPool.pop()||{array:null,cache:null,criteria:null,"false":false,index:0,"null":false,number:null,object:null,push:null,string:null,"true":false,undefined:false,value:null}}function releaseArray(array){array.length=0;if(arrayPool.length<maxPoolSize){arrayPool.push(array)}}function releaseObject(object){var cache=object.cache;if(cache){releaseObject(cache)}object.array=object.cache=object.criteria=object.object=object.number=object.string=object.value=null;if(objectPool.length<maxPoolSize){objectPool.push(object)}}function slice(array,start,end){start||(start=0);if(typeof end=="undefined"){end=array?array.length:0}var index=-1,length=end-start||0,result=Array(length<0?0:length);whi
le(++index<length){result[index]=array[start+index]}return result}function runInContext(context){context=context?_.defaults(root.Object(),context,_.pick(root,contextProps)):root;var Array=context.Array,Boolean=context.Boolean,Date=context.Date,Function=context.Function,Math=context.Math,Number=context.Number,Object=context.Object,RegExp=context.RegExp,String=context.String,TypeError=context.TypeError;var arrayRef=[];var objectProto=Object.prototype;var oldDash=context._;var toString=objectProto.toString;var reNative=RegExp("^"+String(toString).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$");var ceil=Math.ceil,clearTimeout=context.clearTimeout,floor=Math.floor,fnToString=Function.prototype.toString,getPrototypeOf=isNative(getPrototypeOf=Object.getPrototypeOf)&&getPrototypeOf,hasOwnProperty=objectProto.hasOwnProperty,push=arrayRef.push,setTimeout=context.setTimeout,splice=arrayRef.splice,unshift=arrayRef.unshift;var defineProperty=function(){try{var o
={},func=isNative(func=Object.defineProperty)&&func,result=func(o,o,o)&&func}catch(e){}return result}();var nativeCreate=isNative(nativeCreate=Object.create)&&nativeCreate,nativeIsArray=isNative(nativeIsArray=Array.isArray)&&nativeIsArray,nativeIsFinite=context.isFinite,nativeIsNaN=context.isNaN,nativeKeys=isNative(nativeKeys=Object.keys)&&nativeKeys,nativeMax=Math.max,nativeMin=Math.min,nativeParseInt=context.parseInt,nativeRandom=Math.random;var ctorByClass={};ctorByClass[arrayClass]=Array;ctorByClass[boolClass]=Boolean;ctorByClass[dateClass]=Date;ctorByClass[funcClass]=Function;ctorByClass[objectClass]=Object;ctorByClass[numberClass]=Number;ctorByClass[regexpClass]=RegExp;ctorByClass[stringClass]=String;function lodash(value){return value&&typeof value=="object"&&!isArray(value)&&hasOwnProperty.call(value,"__wrapped__")?value:new lodashWrapper(value)}function lodashWrapper(value,chainAll){this.__chain__=!!chainAll;this.__wrapped__=value}lodashWrapper.prototype=lodash.prototype;va
r support=lodash.support={};support.funcDecomp=!isNative(context.WinRTError)&&reThis.test(runInContext);support.funcNames=typeof Function.name=="string";lodash.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function baseBind(bindData){var func=bindData[0],partialArgs=bindData[2],thisArg=bindData[4];function bound(){if(partialArgs){var args=slice(partialArgs);push.apply(args,arguments)}if(this instanceof bound){var thisBinding=baseCreate(func.prototype),result=func.apply(thisBinding,args||arguments);return isObject(result)?result:thisBinding}return func.apply(thisArg,args||arguments)}setBindData(bound,bindData);return bound}function baseClone(value,isDeep,callback,stackA,stackB){if(callback){var result=callback(value);if(typeof result!="undefined"){return result}}var isObj=isObject(value);if(isObj){var className=toString.call(value);if(!cloneableClasses[className]){return value}var ctor=ctorByClass[clas
sName];switch(className){case boolClass:case dateClass:return new ctor(+value);case numberClass:case stringClass:return new ctor(value);case regexpClass:result=ctor(value.source,reFlags.exec(value));result.lastIndex=value.lastIndex;return result}}else{return value}var isArr=isArray(value);if(isDeep){var initedStack=!stackA;stackA||(stackA=getArray());stackB||(stackB=getArray());var length=stackA.length;while(length--){if(stackA[length]==value){return stackB[length]}}result=isArr?ctor(value.length):{}}else{result=isArr?slice(value):assign({},value)}if(isArr){if(hasOwnProperty.call(value,"index")){result.index=value.index}if(hasOwnProperty.call(value,"input")){result.input=value.input}}if(!isDeep){return result}stackA.push(value);stackB.push(result);(isArr?forEach:forOwn)(value,function(objValue,key){result[key]=baseClone(objValue,isDeep,callback,stackA,stackB)});if(initedStack){releaseArray(stackA);releaseArray(stackB)}return result}function baseCreate(prototype,properties){return is
Object(prototype)?nativeCreate(prototype):{}}if(!nativeCreate){baseCreate=function(){function Object(){}return function(prototype){if(isObject(prototype)){Object.prototype=prototype;var result=new Object;Object.prototype=null}return result||context.Object()}}()}function baseCreateCallback(func,thisArg,argCount){if(typeof func!="function"){return identity}if(typeof thisArg=="undefined"||!("prototype"in func)){return func}var bindData=func.__bindData__;if(typeof bindData=="undefined"){if(support.funcNames){bindData=!func.name}bindData=bindData||!support.funcDecomp;if(!bindData){var source=fnToString.call(func);if(!support.funcNames){bindData=!reFuncName.test(source)}if(!bindData){bindData=reThis.test(source);setBindData(func,bindData)}}}if(bindData===false||bindData!==true&&bindData[1]&1){return func}switch(argCount){case 1:return function(value){return func.call(thisArg,value)};case 2:return function(a,b){return func.call(thisArg,a,b)};case 3:return function(value,index,collection){r
eturn func.call(thisArg,value,index,collection)};case 4:return function(accumulator,value,index,collection){return func.call(thisArg,accumulator,value,index,collection)}}return bind(func,thisArg)}function baseCreateWrapper(bindData){var func=bindData[0],bitmask=bindData[1],partialArgs=bindData[2],partialRightArgs=bindData[3],thisArg=bindData[4],arity=bindData[5];var isBind=bitmask&1,isBindKey=bitmask&2,isCurry=bitmask&4,isCurryBound=bitmask&8,key=func;function bound(){var thisBinding=isBind?thisArg:this;if(partialArgs){var args=slice(partialArgs);push.apply(args,arguments)}if(partialRightArgs||isCurry){args||(args=slice(arguments));if(partialRightArgs){push.apply(args,partialRightArgs)}if(isCurry&&args.length<arity){bitmask|=16&~32;return baseCreateWrapper([func,isCurryBound?bitmask:bitmask&~3,args,null,thisArg,arity])}}args||(args=arguments);if(isBindKey){func=thisBinding[key]}if(this instanceof bound){thisBinding=baseCreate(func.prototype);var result=func.apply(thisBinding,args);r
eturn isObject(result)?result:thisBinding}return func.apply(thisBinding,args)}setBindData(bound,bindData);return bound}function baseDifference(array,values){var index=-1,indexOf=getIndexOf(),length=array?array.length:0,isLarge=length>=largeArraySize&&indexOf===baseIndexOf,result=[];if(isLarge){var cache=createCache(values);if(cache){indexOf=cacheIndexOf;values=cache}else{isLarge=false}}while(++index<length){var value=array[index];if(indexOf(values,value)<0){result.push(value)}}if(isLarge){releaseObject(values)}return result}function baseFlatten(array,isShallow,isStrict,fromIndex){var index=(fromIndex||0)-1,length=array?array.length:0,result=[];while(++index<length){var value=array[index];if(value&&typeof value=="object"&&typeof value.length=="number"&&(isArray(value)||isArguments(value))){if(!isShallow){value=baseFlatten(value,isShallow,isStrict)}var valIndex=-1,valLength=value.length,resIndex=result.length;result.length+=valLength;while(++valIndex<valLength){result[resIndex++]=valu
e[valIndex]}}else if(!isStrict){result.push(value)}}return result}function baseIsEqual(a,b,callback,isWhere,stackA,stackB){if(callback){var result=callback(a,b);if(typeof result!="undefined"){return!!result}}if(a===b){return a!==0||1/a==1/b}var type=typeof a,otherType=typeof b;if(a===a&&!(a&&objectTypes[type])&&!(b&&objectTypes[otherType])){return false}if(a==null||b==null){return a===b}var className=toString.call(a),otherClass=toString.call(b);if(className==argsClass){className=objectClass}if(otherClass==argsClass){otherClass=objectClass}if(className!=otherClass){return false}switch(className){case boolClass:case dateClass:return+a==+b;case numberClass:return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case regexpClass:case stringClass:return a==String(b)}var isArr=className==arrayClass;if(!isArr){var aWrapped=hasOwnProperty.call(a,"__wrapped__"),bWrapped=hasOwnProperty.call(b,"__wrapped__");if(aWrapped||bWrapped){return baseIsEqual(aWrapped?a.__wrapped__:a,bWrapped?b.__wrapped__:b,callback,is
Where,stackA,stackB)}if(className!=objectClass){return false}var ctorA=a.constructor,ctorB=b.constructor;if(ctorA!=ctorB&&!(isFunction(ctorA)&&ctorA instanceof ctorA&&isFunction(ctorB)&&ctorB instanceof ctorB)&&("constructor"in a&&"constructor"in b)){return false}}var initedStack=!stackA;stackA||(stackA=getArray());stackB||(stackB=getArray());var length=stackA.length;while(length--){if(stackA[length]==a){return stackB[length]==b}}var size=0;result=true;stackA.push(a);stackB.push(b);if(isArr){length=a.length;size=b.length;result=size==length;if(result||isWhere){while(size--){var index=length,value=b[size];if(isWhere){while(index--){if(result=baseIsEqual(a[index],value,callback,isWhere,stackA,stackB)){break}}}else if(!(result=baseIsEqual(a[size],value,callback,isWhere,stackA,stackB))){break}}}}else{forIn(b,function(value,key,b){if(hasOwnProperty.call(b,key)){size++;return result=hasOwnProperty.call(a,key)&&baseIsEqual(a[key],value,callback,isWhere,stackA,stackB)}});if(result&&!isWhere
){forIn(a,function(value,key,a){if(hasOwnProperty.call(a,key)){return result=--size>-1}})}}stackA.pop();stackB.pop();if(initedStack){releaseArray(stackA);releaseArray(stackB)}return result}function baseMerge(object,source,callback,stackA,stackB){(isArray(source)?forEach:forOwn)(source,function(source,key){var found,isArr,result=source,value=object[key];if(source&&((isArr=isArray(source))||isPlainObject(source))){var stackLength=stackA.length;while(stackLength--){if(found=stackA[stackLength]==source){value=stackB[stackLength];break}}if(!found){var isShallow;if(callback){result=callback(value,source);if(isShallow=typeof result!="undefined"){value=result}}if(!isShallow){value=isArr?isArray(value)?value:[]:isPlainObject(value)?value:{}}stackA.push(source);stackB.push(value);if(!isShallow){baseMerge(value,source,callback,stackA,stackB)}}}else{if(callback){result=callback(value,source);if(typeof result=="undefined"){result=source}}if(typeof result!="undefined"){value=result}}object[key]=v
alue})}function baseRandom(min,max){return min+floor(nativeRandom()*(max-min+1))}function baseUniq(array,isSorted,callback){var index=-1,indexOf=getIndexOf(),length=array?array.length:0,result=[];var isLarge=!isSorted&&length>=largeArraySize&&indexOf===baseIndexOf,seen=callback||isLarge?getArray():result;if(isLarge){var cache=createCache(seen);indexOf=cacheIndexOf;seen=cache}while(++index<length){var value=array[index],computed=callback?callback(value,index,array):value;if(isSorted?!index||seen[seen.length-1]!==computed:indexOf(seen,computed)<0){if(callback||isLarge){seen.push(computed)}result.push(value)}}if(isLarge){releaseArray(seen.array);releaseObject(seen)}else if(callback){releaseArray(seen)}return result}function createAggregator(setter){return function(collection,callback,thisArg){var result={};callback=lodash.createCallback(callback,thisArg,3);var index=-1,length=collection?collection.length:0;if(typeof length=="number"){while(++index<length){var value=collection[index];se
tter(result,value,callback(value,index,collection),collection)}}else{forOwn(collection,function(value,key,collection){setter(result,value,callback(value,key,collection),collection)})}return result}}function createWrapper(func,bitmask,partialArgs,partialRightArgs,thisArg,arity){var isBind=bitmask&1,isBindKey=bitmask&2,isCurry=bitmask&4,isCurryBound=bitmask&8,isPartial=bitmask&16,isPartialRight=bitmask&32;if(!isBindKey&&!isFunction(func)){throw new TypeError}if(isPartial&&!partialArgs.length){bitmask&=~16;isPartial=partialArgs=false}if(isPartialRight&&!partialRightArgs.length){bitmask&=~32;isPartialRight=partialRightArgs=false}var bindData=func&&func.__bindData__;if(bindData&&bindData!==true){bindData=slice(bindData);if(bindData[2]){bindData[2]=slice(bindData[2])}if(bindData[3]){bindData[3]=slice(bindData[3])}if(isBind&&!(bindData[1]&1)){bindData[4]=thisArg}if(!isBind&&bindData[1]&1){bitmask|=8}if(isCurry&&!(bindData[1]&4)){bindData[5]=arity}if(isPartial){push.apply(bindData[2]||(bind
Data[2]=[]),partialArgs)}if(isPartialRight){unshift.apply(bindData[3]||(bindData[3]=[]),partialRightArgs)}bindData[1]|=bitmask;return createWrapper.apply(null,bindData)}var creater=bitmask==1||bitmask===17?baseBind:baseCreateWrapper;return creater([func,bitmask,partialArgs,partialRightArgs,thisArg,arity])}function escapeHtmlChar(match){return htmlEscapes[match]}function getIndexOf(){var result=(result=lodash.indexOf)===indexOf?baseIndexOf:result;return result}function isNative(value){return typeof value=="function"&&reNative.test(value)}var setBindData=!defineProperty?noop:function(func,value){descriptor.value=value;defineProperty(func,"__bindData__",descriptor)};function shimIsPlainObject(value){var ctor,result;if(!(value&&toString.call(value)==objectClass)||(ctor=value.constructor,isFunction(ctor)&&!(ctor instanceof ctor))){return false}forIn(value,function(value,key){result=key});return typeof result=="undefined"||hasOwnProperty.call(value,result)}function unescapeHtmlChar(match)
{return htmlUnescapes[match]}function isArguments(value){return value&&typeof value=="object"&&typeof value.length=="number"&&toString.call(value)==argsClass||false}var isArray=nativeIsArray||function(value){return value&&typeof value=="object"&&typeof value.length=="number"&&toString.call(value)==arrayClass||false};var shimKeys=function(object){var index,iterable=object,result=[];if(!iterable)return result;if(!objectTypes[typeof object])return result;for(index in iterable){if(hasOwnProperty.call(iterable,index)){result.push(index)}}return result};var keys=!nativeKeys?shimKeys:function(object){if(!isObject(object)){return[]}return nativeKeys(object)};var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'"};var htmlUnescapes=invert(htmlEscapes);var reEscapedHtml=RegExp("("+keys(htmlUnescapes).join("|")+")","g"),reUnescapedHtml=RegExp("["+keys(htmlEscapes).join("")+"]","g");var assign=function(object,source,guard){var index,iterable=object,result=iterable;if(!itera
ble)return result;var args=arguments,argsIndex=0,argsLength=typeof guard=="number"?2:args.length;if(argsLength>3&&typeof args[argsLength-2]=="function"){var callback=baseCreateCallback(args[--argsLength-1],args[argsLength--],2)}else if(argsLength>2&&typeof args[argsLength-1]=="function"){callback=args[--argsLength]}while(++argsIndex<argsLength){iterable=args[argsIndex];if(iterable&&objectTypes[typeof iterable]){var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];result[index]=callback?callback(result[index],iterable[index]):iterable[index]}}}return result};function clone(value,isDeep,callback,thisArg){if(typeof isDeep!="boolean"&&isDeep!=null){thisArg=callback;callback=isDeep;isDeep=false}return baseClone(value,isDeep,typeof callback=="function"&&baseCreateCallback(callback,thisArg,1))}function cloneDeep(value,callback,thisArg){return baseClone(value,true,typeof callback=="function"
&&baseCreateCallback(callback,thisArg,1))}function create(prototype,properties){var result=baseCreate(prototype);return properties?assign(result,properties):result}var defaults=function(object,source,guard){var index,iterable=object,result=iterable;if(!iterable)return result;var args=arguments,argsIndex=0,argsLength=typeof guard=="number"?2:args.length;while(++argsIndex<argsLength){iterable=args[argsIndex];if(iterable&&objectTypes[typeof iterable]){var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];if(typeof result[index]=="undefined")result[index]=iterable[index]}}}return result};function findKey(object,callback,thisArg){var result;callback=lodash.createCallback(callback,thisArg,3);forOwn(object,function(value,key,object){if(callback(value,key,object)){result=key;return false}});return result}function findLastKey(object,callback,thisArg){var result;callback=lodash.createCallback(c
allback,thisArg,3);forOwnRight(object,function(value,key,object){if(callback(value,key,object)){result=key;return false}});return result}var forIn=function(collection,callback,thisArg){var index,iterable=collection,result=iterable;if(!iterable)return result;if(!objectTypes[typeof iterable])return result;callback=callback&&typeof thisArg=="undefined"?callback:baseCreateCallback(callback,thisArg,3);for(index in iterable){if(callback(iterable[index],index,collection)===false)return result}return result};function forInRight(object,callback,thisArg){var pairs=[];forIn(object,function(value,key){pairs.push(key,value)});var length=pairs.length;callback=baseCreateCallback(callback,thisArg,3);while(length--){if(callback(pairs[length--],pairs[length],object)===false){break}}return object}var forOwn=function(collection,callback,thisArg){var index,iterable=collection,result=iterable;if(!iterable)return result;if(!objectTypes[typeof iterable])return result;callback=callback&&typeof thisArg=="und
efined"?callback:baseCreateCallback(callback,thisArg,3);var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];if(callback(iterable[index],index,collection)===false)return result}return result};function forOwnRight(object,callback,thisArg){var props=keys(object),length=props.length;callback=baseCreateCallback(callback,thisArg,3);while(length--){var key=props[length];if(callback(object[key],key,object)===false){break}}return object}function functions(object){var result=[];forIn(object,function(value,key){if(isFunction(value)){result.push(key)}});return result.sort()}function has(object,key){return object?hasOwnProperty.call(object,key):false}function invert(object){var index=-1,props=keys(object),length=props.length,result={};while(++index<length){var key=props[index];result[object[key]]=key}return result}function isBoolean(value){return value===true||value===false||value&&typeof value=
="object"&&toString.call(value)==boolClass||false}function isDate(value){return value&&typeof value=="object"&&toString.call(value)==dateClass||false}function isElement(value){return value&&value.nodeType===1||false}function isEmpty(value){var result=true;if(!value){return result}var className=toString.call(value),length=value.length;if(className==arrayClass||className==stringClass||className==argsClass||className==objectClass&&typeof length=="number"&&isFunction(value.splice)){return!length}forOwn(value,function(){return result=false});return result}function isEqual(a,b,callback,thisArg){return baseIsEqual(a,b,typeof callback=="function"&&baseCreateCallback(callback,thisArg,2))}function isFinite(value){return nativeIsFinite(value)&&!nativeIsNaN(parseFloat(value))}function isFunction(value){return typeof value=="function"}function isObject(value){return!!(value&&objectTypes[typeof value])}function isNaN(value){return isNumber(value)&&value!=+value}function isNull(value){return value
===null}function isNumber(value){return typeof value=="number"||value&&typeof value=="object"&&toString.call(value)==numberClass||false}var isPlainObject=!getPrototypeOf?shimIsPlainObject:function(value){if(!(value&&toString.call(value)==objectClass)){return false}var valueOf=value.valueOf,objProto=isNative(valueOf)&&(objProto=getPrototypeOf(valueOf))&&getPrototypeOf(objProto);return objProto?value==objProto||getPrototypeOf(value)==objProto:shimIsPlainObject(value)};function isRegExp(value){return value&&typeof value=="object"&&toString.call(value)==regexpClass||false}function isString(value){return typeof value=="string"||value&&typeof value=="object"&&toString.call(value)==stringClass||false}function isUndefined(value){return typeof value=="undefined"}function mapValues(object,callback,thisArg){var result={};callback=lodash.createCallback(callback,thisArg,3);forOwn(object,function(value,key,object){result[key]=callback(value,key,object)});return result}function merge(object){var a
rgs=arguments,length=2;if(!isObject(object)){return object}if(typeof args[2]!="number"){length=args.length}if(length>3&&typeof args[length-2]=="function"){var callback=baseCreateCallback(args[--length-1],args[length--],2)}else if(length>2&&typeof args[length-1]=="function"){callback=args[--length]}var sources=slice(arguments,1,length),index=-1,stackA=getArray(),stackB=getArray();while(++index<length){baseMerge(object,sources[index],callback,stackA,stackB)}releaseArray(stackA);releaseArray(stackB);return object}function omit(object,callback,thisArg){var result={};if(typeof callback!="function"){var props=[];forIn(object,function(value,key){props.push(key)});props=baseDifference(props,baseFlatten(arguments,true,false,1));var index=-1,length=props.length;while(++index<length){var key=props[index];result[key]=object[key]}}else{callback=lodash.createCallback(callback,thisArg,3);forIn(object,function(value,key,object){if(!callback(value,key,object)){result[key]=value}})}return result}func
tion pairs(object){var index=-1,props=keys(object),length=props.length,result=Array(length);while(++index<length){var key=props[index];result[index]=[key,object[key]]}return result}function pick(object,callback,thisArg){var result={};if(typeof callback!="function
<TRUNCATED>