zeppelin-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m...@apache.org
Subject [1/2] zeppelin git commit: [ZEPPELIN-1619] Load js package as a plugin visualization
Date Sat, 14 Jan 2017 18:27:49 GMT
Repository: zeppelin
Updated Branches:
  refs/heads/branch-0.7 054e0846d -> 8dc4721ea


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/src/app/visualization/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/package.json b/zeppelin-web/src/app/visualization/package.json
new file mode 100644
index 0000000..c0fb9d3
--- /dev/null
+++ b/zeppelin-web/src/app/visualization/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "zeppelin-vis",
+  "description": "Visualization API",
+  "version": "0.7.0-SNAPSHOT",
+  "main": "visualization",
+  "dependencies": {
+    "json3": "~3.3.1",
+    "nvd3": "~1.7.1",
+    "lodash": "~3.9.3"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apache/zeppelin"
+  },
+  "license": "Apache-2.0"
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/src/components/helium/helium.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium.service.js b/zeppelin-web/src/components/helium/helium.service.js
new file mode 100644
index 0000000..ae44425
--- /dev/null
+++ b/zeppelin-web/src/components/helium/helium.service.js
@@ -0,0 +1,62 @@
+/*
+ * Licensed 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.
+ */
+
+(function() {
+
+  angular.module('zeppelinWebApp').service('heliumService', heliumService);
+
+  heliumService.$inject = ['$http', 'baseUrlSrv', 'ngToast'];
+
+  function heliumService($http, baseUrlSrv, ngToast) {
+
+    var url = baseUrlSrv.getRestApiBase() + '/helium/visualizations/load';
+    if (process.env.HELIUM_VIS_DEV) {
+      url = url + '?refresh=true';
+    }
+    var visualizations = [];
+
+    // load should be promise
+    this.load = $http.get(url).success(function(response) {
+      if (response.substring(0, 'ERROR:'.length) !== 'ERROR:') {
+        eval(response);
+      } else {
+        console.error(response);
+      }
+    });
+
+    this.get = function() {
+      return visualizations;
+    };
+
+    this.getVisualizationOrder = function() {
+      return $http.get(baseUrlSrv.getRestApiBase() + '/helium/visualizationOrder');
+    };
+
+    this.setVisualizationOrder = function(list) {
+      return $http.post(baseUrlSrv.getRestApiBase() + '/helium/visualizationOrder', list);
+    };
+
+    this.getAllPackageInfo = function() {
+      return $http.get(baseUrlSrv.getRestApiBase() + '/helium/all');
+    };
+
+    this.enable = function(name, artifact) {
+      return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact);
+    };
+
+    this.disable = function(name) {
+      return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name);
+    };
+  };
+})();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/src/components/navbar/navbar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html
index a477055..cf8b5b6 100644
--- a/zeppelin-web/src/components/navbar/navbar.html
+++ b/zeppelin-web/src/components/navbar/navbar.html
@@ -94,6 +94,7 @@ limitations under the License.
               <li><a href="#/interpreter">Interpreter</a></li>
               <li><a href="#/notebookRepos">Notebook Repos</a></li>
               <li><a href="#/credential">Credential</a></li>
+              <li><a href="#/helium">Helium</a></li>
               <li><a href="#/configuration">Configuration</a></li>
               <li ng-if="ticket.principal && ticket.principal !== 'anonymous'" role="separator" style="margin: 5px 0;" class="divider"></li>
               <li ng-if="ticket.principal && ticket.principal !== 'anonymous'"><a ng-click="navbar.logout()">Logout</a></li>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 3769d65..0bc5129 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -61,6 +61,7 @@ limitations under the License.
     <link rel="stylesheet" href="app/jobmanager/jobmanager.css" />
     <link rel="stylesheet" href="app/jobmanager/jobs/job.css" />
     <link rel="stylesheet" href="app/interpreter/interpreter.css" />
+    <link rel="stylesheet" href="app/helium/helium.css" />
     <link rel="stylesheet" href="app/credential/credential.css" />
     <link rel="stylesheet" href="app/configuration/configuration.css" />
     <link rel="stylesheet" href="components/expandCollapse/expandCollapse.css" />

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/src/index.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.js b/zeppelin-web/src/index.js
index 95d799d..191d2e3 100644
--- a/zeppelin-web/src/index.js
+++ b/zeppelin-web/src/index.js
@@ -45,6 +45,7 @@ import './app/notebook/paragraph/paragraph.controller.js';
 import './app/notebook/paragraph/result/result.controller.js';
 import './app/search/result-list.controller.js';
 import './app/notebookRepos/notebookRepos.controller.js';
+import './app/helium/helium.controller.js';
 import './components/arrayOrderingSrv/arrayOrdering.service.js';
 import './components/clipboard/clipboard.controller.js';
 import './components/navbar/navbar.controller.js';
@@ -73,3 +74,4 @@ import './components/noteAction/noteAction.service.js';
 import './components/notevarshareService/notevarshare.service.js';
 import './components/rename/rename.controller.js';
 import './components/rename/rename.service.js';
+import './components/helium/helium.service.js';

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-web/webpack.config.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/webpack.config.js b/zeppelin-web/webpack.config.js
index 0efc721..d3a2681 100644
--- a/zeppelin-web/webpack.config.js
+++ b/zeppelin-web/webpack.config.js
@@ -205,7 +205,14 @@ module.exports = function makeWebpackConfig () {
       // Reference: https://github.com/webpack/extract-text-webpack-plugin
       // Extract css files
       // Disabled when in test mode or not in build mode
-      new ExtractTextPlugin('[name].[hash].css', {disable: !isProd})
+      new ExtractTextPlugin('[name].[hash].css', {disable: !isProd}),
+
+      // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
+      new webpack.DefinePlugin({
+        'process.env': {
+          HELIUM_VIS_DEV: process.env.HELIUM_VIS_DEV
+        }
+      })
     )
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml
index 07fa2df..d620512 100644
--- a/zeppelin-zengine/pom.xml
+++ b/zeppelin-zengine/pom.xml
@@ -45,6 +45,7 @@
     <org.reflections.version>0.9.8</org.reflections.version>
     <xml.apis.version>1.4.01</xml.apis.version>
     <eclipse.jgit.version>4.1.1.201511131810-r</eclipse.jgit.version>
+    <frontend.maven.plugin.version>1.3</frontend.maven.plugin.version>
 
     <!--test library versions-->
     <google.truth.version>0.27</google.truth.version>
@@ -218,6 +219,26 @@
     </dependency>
 
     <dependency>
+      <groupId>com.github.eirslett</groupId>
+      <artifactId>frontend-maven-plugin</artifactId>
+      <version>${frontend.maven.plugin.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-utils</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-plugin-api</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-artifact</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
@@ -256,7 +277,6 @@
       <version>${jetty.version}</version>
       <scope>test</scope>
     </dependency>
-
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/Helium.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/Helium.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/Helium.java
index 79eb3a6..8ef30c8 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/Helium.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/Helium.java
@@ -16,6 +16,7 @@
  */
 package org.apache.zeppelin.helium;
 
+import com.github.eirslett.maven.plugins.frontend.lib.TaskRunnerException;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import org.apache.commons.io.FileUtils;
@@ -32,10 +33,7 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Manages helium packages
@@ -48,10 +46,19 @@ public class Helium {
   private final String heliumConfPath;
   private final String defaultLocalRegistryPath;
   private final Gson gson;
+  private final HeliumVisualizationFactory visualizationFactory;
+  private final HeliumApplicationFactory applicationFactory;
 
-  public Helium(String heliumConfPath, String defaultLocalRegistryPath) throws IOException {
+  public Helium(
+      String heliumConfPath,
+      String defaultLocalRegistryPath,
+      HeliumVisualizationFactory visualizationFactory,
+      HeliumApplicationFactory applicationFactory)
+      throws IOException, TaskRunnerException {
     this.heliumConfPath = heliumConfPath;
     this.defaultLocalRegistryPath = defaultLocalRegistryPath;
+    this.visualizationFactory = visualizationFactory;
+    this.applicationFactory = applicationFactory;
 
     GsonBuilder builder = new GsonBuilder();
     builder.setPrettyPrinting();
@@ -83,6 +90,14 @@ public class Helium {
     }
   }
 
+  public HeliumApplicationFactory getApplicationFactory() {
+    return applicationFactory;
+  }
+
+  public HeliumVisualizationFactory getVisualizationFactory() {
+    return visualizationFactory;
+  }
+
   private synchronized HeliumConf loadConf(String path) throws IOException {
     File heliumConfFile = new File(path);
     if (!heliumConfFile.isFile()) {
@@ -104,6 +119,7 @@ public class Helium {
   public synchronized void save() throws IOException {
     String jsonString;
     synchronized (registry) {
+      clearNotExistsPackages();
       heliumConf.setRegistry(registry);
       jsonString = gson.toJson(heliumConf);
     }
@@ -116,20 +132,118 @@ public class Helium {
     FileUtils.writeStringToFile(heliumConfFile, jsonString);
   }
 
-  public List<HeliumPackageSearchResult> getAllPackageInfo() {
-    List<HeliumPackageSearchResult> list = new LinkedList<>();
+  private void clearNotExistsPackages() {
+    Map<String, List<HeliumPackageSearchResult>> all = getAllPackageInfo();
+
+    // clear visualization display order
+    List<String> packageOrder = heliumConf.getVisualizationDisplayOrder();
+    List<String> clearedOrder = new LinkedList<>();
+    for (String pkgName : packageOrder) {
+      if (all.containsKey(pkgName)) {
+        clearedOrder.add(pkgName);
+      }
+    }
+    heliumConf.setVisualizationDisplayOrder(clearedOrder);
+
+    // clear enabled package
+    Map<String, String> enabledPackages = heliumConf.getEnabledPackages();
+    for (String pkgName : enabledPackages.keySet()) {
+      if (!all.containsKey(pkgName)) {
+        heliumConf.disablePackage(pkgName);
+      }
+    }
+  }
+
+  public Map<String, List<HeliumPackageSearchResult>> getAllPackageInfo() {
+    Map<String, String> enabledPackageInfo = heliumConf.getEnabledPackages();
+
+    Map<String, List<HeliumPackageSearchResult>> map = new HashMap<>();
     synchronized (registry) {
       for (HeliumRegistry r : registry) {
         try {
           for (HeliumPackage pkg : r.getAll()) {
-            list.add(new HeliumPackageSearchResult(r.name(), pkg));
+            String name = pkg.getName();
+            String artifact = enabledPackageInfo.get(name);
+            boolean enabled = (artifact != null && artifact.equals(pkg.getArtifact()));
+
+            if (!map.containsKey(name)) {
+              map.put(name, new LinkedList<HeliumPackageSearchResult>());
+            }
+            map.get(name).add(new HeliumPackageSearchResult(r.name(), pkg, enabled));
           }
         } catch (IOException e) {
           logger.error(e.getMessage(), e);
         }
       }
     }
-    return list;
+
+    // sort version (artifact)
+    for (String name : map.keySet()) {
+      List<HeliumPackageSearchResult> packages = map.get(name);
+      Collections.sort(packages, new Comparator<HeliumPackageSearchResult>() {
+        @Override
+        public int compare(HeliumPackageSearchResult o1, HeliumPackageSearchResult o2) {
+          return o2.getPkg().getArtifact().compareTo(o1.getPkg().getArtifact());
+        }
+      });
+    }
+    return map;
+  }
+
+  public HeliumPackageSearchResult getPackageInfo(String name, String artifact) {
+    Map<String, List<HeliumPackageSearchResult>> infos = getAllPackageInfo();
+    List<HeliumPackageSearchResult> packages = infos.get(name);
+    if (artifact == null) {
+      return packages.get(0);
+    } else {
+      for (HeliumPackageSearchResult pkg : packages) {
+        if (pkg.getPkg().getArtifact().equals(artifact)) {
+          return pkg;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public File recreateVisualizationBundle() throws IOException, TaskRunnerException {
+    return visualizationFactory.bundle(getVisualizationPackagesToBundle(), true);
+  }
+
+  public void enable(String name, String artifact) throws IOException, TaskRunnerException {
+    HeliumPackageSearchResult pkgInfo = getPackageInfo(name, artifact);
+
+    // no package found.
+    if (pkgInfo == null) {
+      return;
+    }
+
+    // enable package
+    heliumConf.enablePackage(name, artifact);
+
+    // if package is visualization, rebuild bundle
+    if (pkgInfo.getPkg().getType() == HeliumPackage.Type.VISUALIZATION) {
+      visualizationFactory.bundle(getVisualizationPackagesToBundle());
+    }
+
+    save();
+  }
+
+  public void disable(String name) throws IOException, TaskRunnerException {
+    String artifact = heliumConf.getEnabledPackages().get(name);
+
+    if (artifact == null) {
+      return;
+    }
+
+    heliumConf.disablePackage(name);
+
+    HeliumPackageSearchResult pkg = getPackageInfo(name, artifact);
+    if (pkg == null || pkg.getPkg().getType() == HeliumPackage.Type.VISUALIZATION) {
+      visualizationFactory.bundle(getVisualizationPackagesToBundle());
+    }
+
+    save();
   }
 
   public HeliumPackageSuggestion suggestApp(Paragraph paragraph) {
@@ -153,20 +267,89 @@ public class Helium {
       allResources = ResourcePoolUtils.getAllResources();
     }
 
-    for (HeliumPackageSearchResult pkg : getAllPackageInfo()) {
-      ResourceSet resources = ApplicationLoader.findRequiredResourceSet(
-          pkg.getPkg().getResources(),
-          paragraph.getNote().getId(),
-          paragraph.getId(),
-          allResources);
-      if (resources == null) {
-        continue;
-      } else {
-        suggestion.addAvailablePackage(pkg);
+    for (List<HeliumPackageSearchResult> pkgs : getAllPackageInfo().values()) {
+      for (HeliumPackageSearchResult pkg : pkgs) {
+        if (pkg.getPkg().getType() == HeliumPackage.Type.APPLICATION && pkg.isEnabled()) {
+          ResourceSet resources = ApplicationLoader.findRequiredResourceSet(
+              pkg.getPkg().getResources(),
+              paragraph.getNote().getId(),
+              paragraph.getId(),
+              allResources);
+          if (resources == null) {
+            continue;
+          } else {
+            suggestion.addAvailablePackage(pkg);
+          }
+          break;
+        }
       }
     }
 
     suggestion.sort();
     return suggestion;
   }
+
+  /**
+   * Get enabled visualization packages
+   *
+   * @return ordered list of enabled visualization package
+   */
+  public List<HeliumPackage> getVisualizationPackagesToBundle() {
+    Map<String, List<HeliumPackageSearchResult>> allPackages = getAllPackageInfo();
+    List<String> visOrder = heliumConf.getVisualizationDisplayOrder();
+
+    List<HeliumPackage> orderedVisualizationPackages = new LinkedList<>();
+
+    // add enabled packages in visOrder
+    for (String name : visOrder) {
+      List<HeliumPackageSearchResult> versions = allPackages.get(name);
+      if (versions == null) {
+        continue;
+      }
+      for (HeliumPackageSearchResult pkgInfo : versions) {
+        if (pkgInfo.getPkg().getType() == HeliumPackage.Type.VISUALIZATION && pkgInfo.isEnabled()) {
+          orderedVisualizationPackages.add(pkgInfo.getPkg());
+          allPackages.remove(name);
+          break;
+        }
+      }
+    }
+
+    // add enabled packages not in visOrder
+    for (List<HeliumPackageSearchResult> pkgs : allPackages.values()) {
+      for (HeliumPackageSearchResult pkg : pkgs) {
+        if (pkg.getPkg().getType() == HeliumPackage.Type.VISUALIZATION && pkg.isEnabled()) {
+          orderedVisualizationPackages.add(pkg.getPkg());
+          break;
+        }
+      }
+    }
+
+    return orderedVisualizationPackages;
+  }
+
+  /**
+   * Get enabled package list in order
+   * @return
+   */
+  public List<String> getVisualizationPackageOrder() {
+    List orderedPackageList = new LinkedList<>();
+    List<HeliumPackage> packages = getVisualizationPackagesToBundle();
+
+    for (HeliumPackage pkg : packages) {
+      orderedPackageList.add(pkg.getName());
+    }
+
+    return orderedPackageList;
+  }
+
+  public void setVisualizationPackageOrder(List<String> orderedPackageList)
+      throws IOException, TaskRunnerException {
+    heliumConf.setVisualizationDisplayOrder(orderedPackageList);
+
+    // if package is visualization, rebuild bundle
+    visualizationFactory.bundle(getVisualizationPackagesToBundle());
+
+    save();
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumConf.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumConf.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumConf.java
index 2c535d4..d3b0fb4 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumConf.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumConf.java
@@ -16,8 +16,7 @@
  */
 package org.apache.zeppelin.helium;
 
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
 
 /**
  * Helium config. This object will be persisted to conf/heliumc.conf
@@ -25,6 +24,13 @@ import java.util.List;
 public class HeliumConf {
   List<HeliumRegistry> registry = new LinkedList<>();
 
+  // enabled packages {name, version}
+  Map<String, String> enabled = Collections.synchronizedMap(new HashMap<String, String>());
+
+  // enabled visualization package display order
+  List<String> visualizationDisplayOrder = new LinkedList<>();
+
+
   public List<HeliumRegistry> getRegistry() {
     return registry;
   }
@@ -32,4 +38,36 @@ public class HeliumConf {
   public void setRegistry(List<HeliumRegistry> registry) {
     this.registry = registry;
   }
+
+  public Map<String, String> getEnabledPackages() {
+    return new HashMap<>(enabled);
+  }
+
+  public void enablePackage(HeliumPackage pkg) {
+    enablePackage(pkg.getName(), pkg.getArtifact());
+  }
+
+  public void enablePackage(String name, String artifact) {
+    enabled.put(name, artifact);
+  }
+
+  public void disablePackage(HeliumPackage pkg) {
+    disablePackage(pkg.getName());
+  }
+
+  public void disablePackage(String name) {
+    enabled.remove(name);
+  }
+
+  public List<String> getVisualizationDisplayOrder() {
+    if (visualizationDisplayOrder == null) {
+      return new LinkedList<String>();
+    } else {
+      return visualizationDisplayOrder;
+    }
+  }
+
+  public void setVisualizationDisplayOrder(List<String> orderedPackageList) {
+    visualizationDisplayOrder = orderedPackageList;
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumPackageSearchResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumPackageSearchResult.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumPackageSearchResult.java
index 57a9d45..36b6115 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumPackageSearchResult.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumPackageSearchResult.java
@@ -22,15 +22,17 @@ package org.apache.zeppelin.helium;
 public class HeliumPackageSearchResult {
   private final String registry;
   private final HeliumPackage pkg;
+  private final boolean enabled;
 
   /**
    * Create search result item
    * @param registry registry name
    * @param pkg package information
    */
-  public HeliumPackageSearchResult(String registry, HeliumPackage pkg) {
+  public HeliumPackageSearchResult(String registry, HeliumPackage pkg, boolean enabled) {
     this.registry = registry;
     this.pkg = pkg;
+    this.enabled = enabled;
   }
 
   public String getRegistry() {
@@ -40,4 +42,8 @@ public class HeliumPackageSearchResult {
   public HeliumPackage getPkg() {
     return pkg;
   }
+
+  public boolean isEnabled() {
+    return enabled;
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumVisualizationFactory.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumVisualizationFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumVisualizationFactory.java
new file mode 100644
index 0000000..a06c18b
--- /dev/null
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumVisualizationFactory.java
@@ -0,0 +1,351 @@
+/*
+ * 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.zeppelin.helium;
+
+import com.github.eirslett.maven.plugins.frontend.lib.*;
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.google.gson.Gson;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Appender;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.WriterAppender;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * Load helium visualization
+ */
+public class HeliumVisualizationFactory {
+  Logger logger = LoggerFactory.getLogger(HeliumVisualizationFactory.class);
+  private final String NODE_VERSION = "v6.9.1";
+  private final String NPM_VERSION = "3.10.8";
+  private final String DEFAULT_NPM_REGISTRY_URL = "http://registry.npmjs.org/";
+
+  private final FrontendPluginFactory frontEndPluginFactory;
+  private final File workingDirectory;
+  private File tabledataModulePath;
+  private File visualizationModulePath;
+  private Gson gson;
+
+  String bundleCacheKey = "";
+  File currentBundle;
+
+  ByteArrayOutputStream out  = new ByteArrayOutputStream();
+
+  public HeliumVisualizationFactory(
+      File moduleDownloadPath,
+      File tabledataModulePath,
+      File visualizationModulePath) throws TaskRunnerException {
+    this(moduleDownloadPath);
+    this.tabledataModulePath = tabledataModulePath;
+    this.visualizationModulePath = visualizationModulePath;
+  }
+
+  public HeliumVisualizationFactory(File moduleDownloadPath) throws TaskRunnerException {
+    this.workingDirectory = new File(moduleDownloadPath, "vis");
+    File installDirectory = workingDirectory;
+
+    frontEndPluginFactory = new FrontendPluginFactory(
+        workingDirectory, installDirectory);
+
+    currentBundle = new File(workingDirectory, "vis.bundle.cache.js");
+    gson = new Gson();
+    installNodeAndNpm();
+    configureLogger();
+  }
+
+  private void installNodeAndNpm() {
+    try {
+      NPMInstaller npmInstaller = frontEndPluginFactory.getNPMInstaller(getProxyConfig());
+      npmInstaller.setNpmVersion(NPM_VERSION);
+      npmInstaller.install();
+
+      NodeInstaller nodeInstaller = frontEndPluginFactory.getNodeInstaller(getProxyConfig());
+      nodeInstaller.setNodeVersion(NODE_VERSION);
+      nodeInstaller.install();
+    } catch (InstallationException e) {
+      logger.error(e.getMessage(), e);
+    }
+  }
+
+  private ProxyConfig getProxyConfig() {
+    List<ProxyConfig.Proxy> proxy = new LinkedList<>();
+    return new ProxyConfig(proxy);
+  }
+
+  public File bundle(List<HeliumPackage> pkgs) throws IOException, TaskRunnerException {
+    return bundle(pkgs, false);
+  }
+
+  public synchronized File bundle(List<HeliumPackage> pkgs, boolean forceRefresh)
+      throws IOException, TaskRunnerException {
+    // package.json
+    URL pkgUrl = Resources.getResource("helium/package.json");
+    String pkgJson = Resources.toString(pkgUrl, Charsets.UTF_8);
+    StringBuilder dependencies = new StringBuilder();
+
+    FileFilter npmPackageCopyFilter = new FileFilter() {
+      @Override
+      public boolean accept(File pathname) {
+        String fileName = pathname.getName();
+        if (fileName.startsWith(".") || fileName.startsWith("#") || fileName.startsWith("~")) {
+          return false;
+        } else {
+          return true;
+        }
+      }
+    };
+
+    for (HeliumPackage pkg : pkgs) {
+      String[] moduleNameVersion = getNpmModuleNameAndVersion(pkg);
+      if (moduleNameVersion == null) {
+        logger.error("Can't get module name and version of package " + pkg.getName());
+        continue;
+      }
+      if (dependencies.length() > 0) {
+        dependencies.append(",\n");
+      }
+      dependencies.append("\"" + moduleNameVersion[0] + "\": \"" + moduleNameVersion[1] + "\"");
+
+      if (isLocalPackage(pkg)) {
+        FileUtils.copyDirectory(
+            new File(pkg.getArtifact()),
+            new File(workingDirectory, "node_modules/" + pkg.getName()),
+            npmPackageCopyFilter);
+      }
+    }
+    pkgJson = pkgJson.replaceFirst("DEPENDENCIES", dependencies.toString());
+
+    // check if we can use previous bundle or not
+    if (dependencies.toString().equals(bundleCacheKey) && currentBundle.isFile() && !forceRefresh) {
+      return currentBundle;
+    }
+
+    // webpack.config.js
+    URL webpackConfigUrl = Resources.getResource("helium/webpack.config.js");
+    String webpackConfig = Resources.toString(webpackConfigUrl, Charsets.UTF_8);
+
+    // generate load.js
+    StringBuilder loadJsImport = new StringBuilder();
+    StringBuilder loadJsRegister = new StringBuilder();
+
+    long idx = 0;
+    for (HeliumPackage pkg : pkgs) {
+      String[] moduleNameVersion = getNpmModuleNameAndVersion(pkg);
+      if (moduleNameVersion == null) {
+        continue;
+      }
+
+      String className = "vis" + idx++;
+      loadJsImport.append(
+          "import " + className + " from \"" + moduleNameVersion[0] + "\"\n");
+
+      loadJsRegister.append("visualizations.push({\n");
+      loadJsRegister.append("id: \"" + moduleNameVersion[0] + "\",\n");
+      loadJsRegister.append("name: \"" + pkg.getName() + "\",\n");
+      loadJsRegister.append("icon: " + gson.toJson(pkg.getIcon()) + ",\n");
+      loadJsRegister.append("class: " + className + "\n");
+      loadJsRegister.append("})\n");
+    }
+
+    FileUtils.write(new File(workingDirectory, "package.json"), pkgJson);
+    FileUtils.write(new File(workingDirectory, "webpack.config.js"), webpackConfig);
+    FileUtils.write(new File(workingDirectory, "load.js"),
+        loadJsImport.append(loadJsRegister).toString());
+
+    // install tabledata module
+    File tabledataModuleInstallPath = new File(workingDirectory,
+        "node_modules/zeppelin-tabledata");
+    if (tabledataModulePath != null && !tabledataModuleInstallPath.exists()) {
+      FileUtils.copyDirectory(
+          tabledataModulePath,
+          tabledataModuleInstallPath,
+          npmPackageCopyFilter);
+    }
+
+    // install visualization module
+    File visModuleInstallPath = new File(workingDirectory,
+        "node_modules/zeppelin-vis");
+    if (visualizationModulePath != null && !visModuleInstallPath.exists()) {
+      FileUtils.copyDirectory(visualizationModulePath, visModuleInstallPath, npmPackageCopyFilter);
+    }
+
+    out.reset();
+    npmCommand("install");
+    npmCommand("run bundle");
+
+    File visBundleJs = new File(workingDirectory, "vis.bundle.js");
+    if (!visBundleJs.isFile()) {
+      throw new IOException(
+          "Can't create visualization bundle : \n" + new String(out.toByteArray()));
+    }
+
+    WebpackResult result = getWebpackResultFromOutput(new String(out.toByteArray()));
+    if (result.errors.length > 0) {
+      visBundleJs.delete();
+      throw new IOException(result.errors[0]);
+    }
+
+    synchronized (this) {
+      currentBundle.delete();
+      FileUtils.moveFile(visBundleJs, currentBundle);
+      bundleCacheKey = dependencies.toString();
+    }
+    return currentBundle;
+  }
+
+  private WebpackResult getWebpackResultFromOutput(String output) {
+    BufferedReader reader = new BufferedReader(new StringReader(output));
+
+    String line;
+    boolean webpackRunDetected = false;
+    boolean resultJsonDetected = false;
+    StringBuffer sb = new StringBuffer();
+    try {
+      while ((line = reader.readLine()) != null) {
+        if (!webpackRunDetected) {
+          if (line.contains("webpack.js") && line.endsWith("--json")) {
+            webpackRunDetected = true;
+          }
+          continue;
+        }
+
+        if (!resultJsonDetected) {
+          if (line.equals("{")) {
+            sb.append(line);
+            resultJsonDetected = true;
+          }
+          continue;
+        }
+
+        if (resultJsonDetected && webpackRunDetected) {
+          sb.append(line);
+        }
+      }
+
+      Gson gson = new Gson();
+      return gson.fromJson(sb.toString(), WebpackResult.class);
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
+      return new WebpackResult();
+    }
+  }
+
+  public File getCurrentBundle() {
+    synchronized (this) {
+      if (currentBundle.isFile()) {
+        return currentBundle;
+      } else {
+        return null;
+      }
+    }
+  }
+
+  private boolean isLocalPackage(HeliumPackage pkg) {
+    return (pkg.getArtifact().startsWith(".") || pkg.getArtifact().startsWith("/"));
+  }
+
+  private String[] getNpmModuleNameAndVersion(HeliumPackage pkg) {
+    String artifact = pkg.getArtifact();
+
+    if (isLocalPackage(pkg)) {
+      File packageJson = new File(artifact, "package.json");
+      if (!packageJson.isFile()) {
+        return null;
+      }
+      Gson gson = new Gson();
+      try {
+        NpmPackage npmPackage = gson.fromJson(
+            FileUtils.readFileToString(packageJson),
+            NpmPackage.class);
+
+        String[] nameVersion = new String[2];
+        nameVersion[0] = npmPackage.name;
+        nameVersion[1] = npmPackage.version;
+        return nameVersion;
+      } catch (IOException e) {
+        logger.error(e.getMessage(), e);
+        return null;
+      }
+    } else {
+      String[] nameVersion = new String[2];
+
+      int pos;
+      if ((pos = artifact.indexOf('@')) > 0) {
+        nameVersion[0] = artifact.substring(0, pos);
+        nameVersion[1] = artifact.substring(pos + 1);
+      } else if (
+          (pos = artifact.indexOf('^')) > 0 ||
+              (pos = artifact.indexOf('~')) > 0) {
+        nameVersion[0] = artifact.substring(0, pos);
+        nameVersion[1] = artifact.substring(pos);
+      } else {
+        nameVersion[0] = artifact;
+        nameVersion[1] = "";
+      }
+      return nameVersion;
+    }
+  }
+
+  public synchronized void install(HeliumPackage pkg) throws TaskRunnerException {
+    npmCommand("install " + pkg.getArtifact());
+  }
+
+  private void npmCommand(String args) throws TaskRunnerException {
+    npmCommand(args, new HashMap<String, String>());
+  }
+
+  private void npmCommand(String args, Map<String, String> env) throws TaskRunnerException {
+    NpmRunner npm = frontEndPluginFactory.getNpmRunner(getProxyConfig(), DEFAULT_NPM_REGISTRY_URL);
+
+    npm.execute(args, env);
+  }
+
+  private void configureLogger() {
+    org.apache.log4j.Logger npmLogger = org.apache.log4j.Logger.getLogger(
+        "com.github.eirslett.maven.plugins.frontend.lib.DefaultNpmRunner");
+    Enumeration appenders = org.apache.log4j.Logger.getRootLogger().getAllAppenders();
+
+    if (appenders != null) {
+      while (appenders.hasMoreElements()) {
+        Appender appender = (Appender) appenders.nextElement();
+        appender.addFilter(new Filter() {
+
+          @Override
+          public int decide(LoggingEvent loggingEvent) {
+            if (loggingEvent.getLoggerName().contains("DefaultNpmRunner")) {
+              return DENY;
+            } else {
+              return NEUTRAL;
+            }
+          }
+        });
+      }
+    }
+    npmLogger.addAppender(new WriterAppender(
+        new PatternLayout("%m%n"),
+        out
+    ));
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/NpmPackage.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/NpmPackage.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/NpmPackage.java
new file mode 100644
index 0000000..271f73f
--- /dev/null
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/NpmPackage.java
@@ -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.
+ */
+package org.apache.zeppelin.helium;
+
+import java.util.Map;
+
+/**
+ * To read package.json
+ */
+public class NpmPackage {
+  public String name;
+  public String version;
+  public Map<String, String> dependencies;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/WebpackResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/WebpackResult.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/WebpackResult.java
new file mode 100644
index 0000000..3414209
--- /dev/null
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/WebpackResult.java
@@ -0,0 +1,25 @@
+/*
+ * 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.zeppelin.helium;
+
+/**
+ * Represetns webpack json format result
+ */
+public class WebpackResult {
+  public final String [] errors = new String[0];
+  public final String [] warnings = new String[0];
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/resources/helium/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/resources/helium/package.json b/zeppelin-zengine/src/main/resources/helium/package.json
new file mode 100644
index 0000000..e6ec612
--- /dev/null
+++ b/zeppelin-zengine/src/main/resources/helium/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "zeppelin-vis-bundle",
+  "main": "load",
+  "scripts": {
+    "bundle": "node/node node_modules/webpack/bin/webpack.js --display-error-details --json"
+  },
+  "dependencies": {
+    DEPENDENCIES
+  },
+  "devDependencies": {
+    "webpack": "^1.12.2",
+    "babel": "^5.8.23",
+    "babel-loader": "^5.3.2"
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/main/resources/helium/webpack.config.js
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/resources/helium/webpack.config.js b/zeppelin-zengine/src/main/resources/helium/webpack.config.js
new file mode 100644
index 0000000..80b8c6a
--- /dev/null
+++ b/zeppelin-zengine/src/main/resources/helium/webpack.config.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+module.exports = {
+    entry: ['./'],
+    output: {
+        path: './',
+        filename: 'vis.bundle.js',
+    },
+    resolve: {
+        root: __dirname + "/node_modules",
+        extensions: [".js"]
+    },
+    module: {
+        loaders: [{
+            test: /\.js$/,
+            //exclude: /node_modules/,
+            loader: 'babel-loader'
+        }]
+    }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
index 73c78d3..2588c4c 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
@@ -134,7 +134,8 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
         "desc1",
         "",
         HeliumTestApplication.class.getName(),
-        new String[][]{});
+        new String[][]{},
+        "", "");
 
     Note note1 = notebook.createNote(anonymous);
     factory.setInterpreters("user", note1.getId(),factory.getDefaultInterpreterSettingList());
@@ -179,7 +180,8 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
         "desc1",
         "",
         HeliumTestApplication.class.getName(),
-        new String[][]{});
+        new String[][]{},
+        "", "");
 
     Note note1 = notebook.createNote(anonymous);
     factory.setInterpreters("user", note1.getId(), factory.getDefaultInterpreterSettingList());
@@ -218,7 +220,8 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
         "desc1",
         "",
         HeliumTestApplication.class.getName(),
-        new String[][]{});
+        new String[][]{},
+        "", "");
 
     Note note1 = notebook.createNote(anonymous);
     notebook.bindInterpretersToNote("user", note1.getId(), factory.getDefaultInterpreterSettingList());
@@ -278,7 +281,8 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
         "desc1",
         "",
         HeliumTestApplication.class.getName(),
-        new String[][]{});
+        new String[][]{},
+        "", "");
 
     Note note1 = notebook.createNote(anonymous);
     notebook.bindInterpretersToNote("user", note1.getId(), factory.getDefaultInterpreterSettingList());

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumLocalRegistryTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumLocalRegistryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumLocalRegistryTest.java
index e90cbc3..03d77b7 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumLocalRegistryTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumLocalRegistryTest.java
@@ -55,7 +55,9 @@ public class HeliumLocalRegistryTest {
         "desc1",
         "artifact1",
         "classname1",
-        new String[][]{});
+        new String[][]{},
+        "license",
+        "");
     FileUtils.writeStringToFile(new File(r1Path, "pkg1.json"), gson.toJson(pkg1));
 
     // then

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java
index 1ed31c5..7c168d0 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.zeppelin.helium;
 
+import com.github.eirslett.maven.plugins.frontend.lib.TaskRunnerException;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -48,10 +49,11 @@ public class HeliumTest {
   }
 
   @Test
-  public void testSaveLoadConf() throws IOException, URISyntaxException {
+  public void testSaveLoadConf() throws IOException, URISyntaxException, TaskRunnerException {
     // given
     File heliumConf = new File(tmpDir, "helium.conf");
-    Helium helium = new Helium(heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath());
+    Helium helium = new Helium(heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(),
+        null, null);
     assertFalse(heliumConf.exists());
     HeliumTestRegistry registry1 = new HeliumTestRegistry("r1", "r1");
     helium.addRegistry(registry1);
@@ -65,14 +67,16 @@ public class HeliumTest {
     assertTrue(heliumConf.exists());
 
     // then
-    Helium heliumRestored = new Helium(heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath());
+    Helium heliumRestored = new Helium(
+        heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null);
     assertEquals(2, heliumRestored.getAllRegistry().size());
   }
 
   @Test
-  public void testRestoreRegistryInstances() throws IOException, URISyntaxException {
+  public void testRestoreRegistryInstances() throws IOException, URISyntaxException, TaskRunnerException {
     File heliumConf = new File(tmpDir, "helium.conf");
-    Helium helium = new Helium(heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath());
+    Helium helium = new Helium(
+        heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null);
     HeliumTestRegistry registry1 = new HeliumTestRegistry("r1", "r1");
     HeliumTestRegistry registry2 = new HeliumTestRegistry("r2", "r2");
     helium.addRegistry(registry1);
@@ -85,7 +89,9 @@ public class HeliumTest {
         "desc1",
         "artifact1",
         "className1",
-        new String[][]{}));
+        new String[][]{},
+        "",
+        ""));
 
     registry2.add(new HeliumPackage(
         HeliumPackage.Type.APPLICATION,
@@ -93,7 +99,9 @@ public class HeliumTest {
         "desc2",
         "artifact2",
         "className2",
-        new String[][]{}));
+        new String[][]{},
+        "",
+        ""));
 
     // then
     assertEquals(2, helium.getAllPackageInfo().size());

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumVisualizationFactoryTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumVisualizationFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumVisualizationFactoryTest.java
new file mode 100644
index 0000000..47af409
--- /dev/null
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumVisualizationFactoryTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.zeppelin.helium;
+
+import com.github.eirslett.maven.plugins.frontend.lib.InstallationException;
+import com.github.eirslett.maven.plugins.frontend.lib.TaskRunnerException;
+import com.google.common.io.Resources;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class HeliumVisualizationFactoryTest {
+  private File tmpDir;
+  private HeliumVisualizationFactory hvf;
+
+  @Before
+  public void setUp() throws InstallationException, TaskRunnerException {
+    tmpDir = new File(System.getProperty("java.io.tmpdir") + "/ZeppelinLTest_" + System.currentTimeMillis());
+    tmpDir.mkdirs();
+
+    // get module dir
+    URL res = Resources.getResource("helium/webpack.config.js");
+    String resDir = new File(res.getFile()).getParent();
+    File moduleDir = new File(resDir + "/../../../../zeppelin-web/src/app/");
+
+    hvf = new HeliumVisualizationFactory(tmpDir,
+        new File(moduleDir, "tabledata"),
+        new File(moduleDir, "visualization"));
+  }
+
+  @After
+  public void tearDown() throws IOException {
+    FileUtils.deleteDirectory(tmpDir);
+  }
+
+  @Test
+  public void testInstallNpm() throws InstallationException {
+    assertTrue(new File(tmpDir, "vis/node/npm").isFile());
+    assertTrue(new File(tmpDir, "vis/node/node").isFile());
+  }
+
+  @Test
+  public void downloadPackage() throws TaskRunnerException {
+    HeliumPackage pkg = new HeliumPackage(
+        HeliumPackage.Type.VISUALIZATION,
+        "lodash",
+        "lodash",
+        "lodash@3.9.3",
+        "",
+        null,
+        "license",
+        "icon"
+    );
+    hvf.install(pkg);
+    assertTrue(new File(tmpDir, "vis/node_modules/lodash").isDirectory());
+  }
+
+  @Test
+  public void bundlePackage() throws IOException, TaskRunnerException {
+    HeliumPackage pkg = new HeliumPackage(
+        HeliumPackage.Type.VISUALIZATION,
+        "zeppelin-bubblechart",
+        "zeppelin-bubblechart",
+        "zeppelin-bubblechart@0.0.3",
+        "",
+        null,
+        "license",
+        "icon"
+    );
+    List<HeliumPackage> pkgs = new LinkedList<>();
+    pkgs.add(pkg);
+    File bundle = hvf.bundle(pkgs);
+    assertTrue(bundle.isFile());
+    long lastModified = bundle.lastModified();
+
+    // bundle again and check if it served from cache
+    bundle = hvf.bundle(pkgs);
+    assertEquals(lastModified, bundle.lastModified());
+  }
+
+
+  @Test
+  public void bundleLocalPackage() throws IOException, TaskRunnerException {
+    URL res = Resources.getResource("helium/webpack.config.js");
+    String resDir = new File(res.getFile()).getParent();
+    String localPkg = resDir + "/../../../src/test/resources/helium/vis1";
+
+    HeliumPackage pkg = new HeliumPackage(
+        HeliumPackage.Type.VISUALIZATION,
+        "vis1",
+        "vis1",
+        localPkg,
+        "",
+        null,
+        "license",
+        "fa fa-coffee"
+    );
+    List<HeliumPackage> pkgs = new LinkedList<>();
+    pkgs.add(pkg);
+    File bundle = hvf.bundle(pkgs);
+    assertTrue(bundle.isFile());
+  }
+
+  @Test
+  public void bundleErrorPropagation() throws IOException, TaskRunnerException {
+    URL res = Resources.getResource("helium/webpack.config.js");
+    String resDir = new File(res.getFile()).getParent();
+    String localPkg = resDir + "/../../../src/test/resources/helium/vis2";
+
+    HeliumPackage pkg = new HeliumPackage(
+        HeliumPackage.Type.VISUALIZATION,
+        "vis2",
+        "vis2",
+        localPkg,
+        "",
+        null,
+        "license",
+        "fa fa-coffee"
+    );
+    List<HeliumPackage> pkgs = new LinkedList<>();
+    pkgs.add(pkg);
+    File bundle = null;
+    try {
+      bundle = hvf.bundle(pkgs);
+      // should throw exception
+      assertTrue(false);
+    } catch (IOException e) {
+      assertTrue(e.getMessage().contains("error in the package"));
+    }
+    assertNull(bundle);
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/resources/helium/vis1/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/resources/helium/vis1/package.json b/zeppelin-zengine/src/test/resources/helium/vis1/package.json
new file mode 100644
index 0000000..481ad58
--- /dev/null
+++ b/zeppelin-zengine/src/test/resources/helium/vis1/package.json
@@ -0,0 +1,12 @@
+{
+  "name": "vis1",
+  "version": "1.0.0",
+  "description": "",
+  "main": "vis1",
+  "author": "",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "zeppelin-tabledata": "*",
+    "zeppelin-vis": "*"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/resources/helium/vis1/vis1.js
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/resources/helium/vis1/vis1.js b/zeppelin-zengine/src/test/resources/helium/vis1/vis1.js
new file mode 100644
index 0000000..98bd552
--- /dev/null
+++ b/zeppelin-zengine/src/test/resources/helium/vis1/vis1.js
@@ -0,0 +1,37 @@
+/*
+ * 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 Visualization from 'zeppelin-vis'
+import PassthroughTransformation from 'zeppelin-tabledata/passthrough'
+
+/**
+ * Base class for visualization
+ */
+export default class vis1 extends Visualization {
+  constructor(targetEl, config) {
+    super(targetEl, config)
+    this.passthrough = new PassthroughTransformation(config);
+    console.log('passthrough %o', this.passthrough);
+  }
+
+  render(tableData) {
+    this.targetEl.html('Vis1')
+  }
+
+  getTransformation() {
+    return this.passthrough
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/resources/helium/vis2/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/resources/helium/vis2/package.json b/zeppelin-zengine/src/test/resources/helium/vis2/package.json
new file mode 100644
index 0000000..b45ee6b
--- /dev/null
+++ b/zeppelin-zengine/src/test/resources/helium/vis2/package.json
@@ -0,0 +1,12 @@
+{
+  "name": "vis2",
+  "version": "1.0.0",
+  "description": "",
+  "main": "vis2",
+  "author": "",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "zeppelin-tabledata": "*",
+    "zeppelin-vis": "*"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/8dc4721e/zeppelin-zengine/src/test/resources/helium/vis2/vis2.js
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/resources/helium/vis2/vis2.js b/zeppelin-zengine/src/test/resources/helium/vis2/vis2.js
new file mode 100644
index 0000000..e9cd324
--- /dev/null
+++ b/zeppelin-zengine/src/test/resources/helium/vis2/vis2.js
@@ -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.
+ */
+import Visualization from 'zeppelin-vis'
+import PassthroughTransformation from 'zeppelin-tabledata/passthrough'
+
+/**
+ * Base class for visualization
+ */
+export default class vis2 extends Visualization {
+  constructor(targetEl, config) {
+    super(targetEl, config)
+    this.passthrough = new PassthroughTransformation(config);
+
+  }
+
+  render(tableData) {
+    this.targetEl.html('Vis2')
+    error in the package
+  }
+
+  getTransformation() {
+    return this.passthrough
+  }
+}


Mime
View raw message