mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Propagate bundle error to the front-end
This commit is contained in:
parent
0fe5e00402
commit
47de6d9684
11 changed files with 220 additions and 32 deletions
|
|
@ -129,7 +129,7 @@ public class HeliumRestApi {
|
|||
// returning error will prevent zeppelin front-end render any notebook.
|
||||
// visualization load fail doesn't need to block notebook rendering work.
|
||||
// so it's better return ok instead of any error.
|
||||
return Response.ok().build();
|
||||
return Response.ok("ERROR: " + e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,10 +142,10 @@ public class HeliumRestApi {
|
|||
return new JsonResponse(Response.Status.OK).build();
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
} catch (TaskRunnerException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,10 +157,10 @@ public class HeliumRestApi {
|
|||
return new JsonResponse(Response.Status.OK).build();
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
} catch (TaskRunnerException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,11 @@ public class ZeppelinServer extends Application {
|
|||
heliumApplicationFactory);
|
||||
|
||||
// create visualization bundle
|
||||
heliumVisualizationFactory.bundle(helium.getVisualizationPackagesToBundle());
|
||||
try {
|
||||
heliumVisualizationFactory.bundle(helium.getVisualizationPackagesToBundle());
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
this.replFactory = new InterpreterFactory(conf, notebookWsServer,
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
}).
|
||||
error(function(data, status) {
|
||||
confirm.close();
|
||||
console.log('Failed to enable package %o %o', name, artifact);
|
||||
console.log('Failed to enable package %o %o. %o', name, artifact, data);
|
||||
BootstrapDialog.show({
|
||||
title: 'Error on enabling ' + name,
|
||||
message: data.message
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
}).
|
||||
error(function(data, status) {
|
||||
confirm.close();
|
||||
console.log('Failed to disable package %o', name);
|
||||
console.log('Failed to disable package %o. %o', name, data);
|
||||
BootstrapDialog.show({
|
||||
title: 'Error on disabling ' + name,
|
||||
message: data.message
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
angular.module('zeppelinWebApp').service('heliumService', heliumService);
|
||||
|
||||
heliumService.$inject = ['$http', 'baseUrlSrv'];
|
||||
heliumService.$inject = ['$http', 'baseUrlSrv', 'ngToast'];
|
||||
|
||||
function heliumService($http, baseUrlSrv) {
|
||||
function heliumService($http, baseUrlSrv, ngToast) {
|
||||
|
||||
var url = baseUrlSrv.getRestApiBase() + '/helium/visualizations/load';
|
||||
if (process.env.HELIUM_VIS_DEV) {
|
||||
|
|
@ -28,7 +28,11 @@
|
|||
|
||||
// load should be promise
|
||||
this.load = $http.get(url).success(function(response) {
|
||||
eval(response);
|
||||
if (response.substring(0, 'ERROR:'.length) !== 'ERROR:') {
|
||||
eval(response);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
});
|
||||
|
||||
this.get = function() {
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ public class Helium {
|
|||
}
|
||||
|
||||
heliumConf.disablePackage(name);
|
||||
|
||||
HeliumPackageSearchResult pkg = getPackageInfo(name, artifact);
|
||||
if (pkg == null || pkg.getPkg().getType() == HeliumPackage.Type.VISUALIZATION) {
|
||||
visualizationFactory.bundle(getVisualizationPackagesToBundle());
|
||||
|
|
|
|||
|
|
@ -21,15 +21,12 @@ 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.slf4j.ILoggerFactory;
|
||||
import org.apache.log4j.PatternLayout;
|
||||
import org.apache.log4j.WriterAppender;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -54,6 +51,8 @@ public class HeliumVisualizationFactory {
|
|||
String bundleCacheKey = "";
|
||||
File currentBundle;
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
public HeliumVisualizationFactory(
|
||||
File moduleDownloadPath,
|
||||
File tabledataModulePath,
|
||||
|
|
@ -74,6 +73,7 @@ public class HeliumVisualizationFactory {
|
|||
currentBundle = new File(workingDirectory, "vis.bundle.cache.js");
|
||||
gson = new Gson();
|
||||
installNodeAndNpm();
|
||||
configureLogger();
|
||||
}
|
||||
|
||||
private void installNodeAndNpm() throws InstallationException, TaskRunnerException {
|
||||
|
|
@ -95,13 +95,25 @@ public class HeliumVisualizationFactory {
|
|||
return bundle(pkgs, false);
|
||||
}
|
||||
|
||||
public File bundle(List<HeliumPackage> pkgs, boolean forceRefresh)
|
||||
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) {
|
||||
|
|
@ -114,8 +126,10 @@ public class HeliumVisualizationFactory {
|
|||
dependencies.append("\"" + moduleNameVersion[0] + "\": \"" + moduleNameVersion[1] + "\"");
|
||||
|
||||
if (isLocalPackage(pkg)) {
|
||||
FileUtils.copyDirectory(new File(pkg.getArtifact()),
|
||||
new File(workingDirectory, "node_modules/" + pkg.getName()));
|
||||
FileUtils.copyDirectory(
|
||||
new File(pkg.getArtifact()),
|
||||
new File(workingDirectory, "node_modules/" + pkg.getName()),
|
||||
npmPackageCopyFilter);
|
||||
}
|
||||
}
|
||||
pkgJson = pkgJson.replaceFirst("DEPENDENCIES", dependencies.toString());
|
||||
|
|
@ -133,19 +147,22 @@ public class HeliumVisualizationFactory {
|
|||
StringBuilder loadJsImport = new StringBuilder();
|
||||
StringBuilder loadJsRegister = new StringBuilder();
|
||||
|
||||
long idx = 0;
|
||||
for (HeliumPackage pkg : pkgs) {
|
||||
String [] moduleNameVersion = getNpmModuleNameAndVersion(pkg);
|
||||
String[] moduleNameVersion = getNpmModuleNameAndVersion(pkg);
|
||||
if (moduleNameVersion == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String className = "vis" + idx++;
|
||||
loadJsImport.append(
|
||||
"import " + moduleNameVersion[0] + " from \"" + moduleNameVersion[0] + "\"\n");
|
||||
"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: " + moduleNameVersion[0] + "\n");
|
||||
loadJsRegister.append("class: " + className + "\n");
|
||||
loadJsRegister.append("})\n");
|
||||
}
|
||||
|
||||
|
|
@ -158,22 +175,30 @@ public class HeliumVisualizationFactory {
|
|||
File tabledataModuleInstallPath = new File(workingDirectory,
|
||||
"node_modules/zeppelin-tabledata");
|
||||
if (tabledataModulePath != null && !tabledataModuleInstallPath.exists()) {
|
||||
FileUtils.copyDirectory(tabledataModulePath, tabledataModuleInstallPath);
|
||||
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);
|
||||
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("Failed to create visualization bundle");
|
||||
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) {
|
||||
|
|
@ -184,6 +209,43 @@ public class HeliumVisualizationFactory {
|
|||
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()) {
|
||||
|
|
@ -198,7 +260,7 @@ public class HeliumVisualizationFactory {
|
|||
return (pkg.getArtifact().startsWith(".") || pkg.getArtifact().startsWith("/"));
|
||||
}
|
||||
|
||||
private String [] getNpmModuleNameAndVersion(HeliumPackage pkg) {
|
||||
private String[] getNpmModuleNameAndVersion(HeliumPackage pkg) {
|
||||
String artifact = pkg.getArtifact();
|
||||
|
||||
if (isLocalPackage(pkg)) {
|
||||
|
|
@ -247,8 +309,20 @@ public class HeliumVisualizationFactory {
|
|||
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");
|
||||
|
||||
npmLogger.addAppender(new WriterAppender(
|
||||
new PatternLayout("%m%n"),
|
||||
out
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "zeppelin-vis-bundle",
|
||||
"main": "load",
|
||||
"scripts": {
|
||||
"bundle": "node/node node_modules/webpack/bin/webpack.js --display-error-details"
|
||||
"bundle": "node/node node_modules/webpack/bin/webpack.js --display-error-details --json"
|
||||
},
|
||||
"dependencies": {
|
||||
DEPENDENCIES
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ 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 {
|
||||
|
|
@ -39,8 +40,9 @@ public class HeliumVisualizationFactoryTest {
|
|||
|
||||
@Before
|
||||
public void setUp() throws InstallationException, TaskRunnerException {
|
||||
tmpDir = new File(System.getProperty("java.io.tmpdir") + "/ZeppelinLTest_" + System.currentTimeMillis());
|
||||
tmpDir.mkdirs();
|
||||
//tmpDir = new File(System.getProperty("java.io.tmpdir") + "/ZeppelinLTest_" + System.currentTimeMillis());
|
||||
tmpDir = new File("/tmp/npm");
|
||||
//tmpDir.mkdirs();
|
||||
|
||||
// get module dir
|
||||
URL res = Resources.getResource("helium/webpack.config.js");
|
||||
|
|
@ -54,7 +56,7 @@ public class HeliumVisualizationFactoryTest {
|
|||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
FileUtils.deleteDirectory(tmpDir);
|
||||
//FileUtils.deleteDirectory(tmpDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -84,7 +86,7 @@ public class HeliumVisualizationFactoryTest {
|
|||
HeliumPackage.Type.VISUALIZATION,
|
||||
"lodash",
|
||||
"lodash",
|
||||
"lodash^3.9.3",
|
||||
"lodash^1.8.3",
|
||||
"",
|
||||
null,
|
||||
"icon"
|
||||
|
|
@ -121,4 +123,32 @@ public class HeliumVisualizationFactoryTest {
|
|||
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,
|
||||
"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) {
|
||||
e.getMessage().contains("error in the package");
|
||||
}
|
||||
assertNull(bundle);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
zeppelin-zengine/src/test/resources/helium/vis2/package.json
Normal file
12
zeppelin-zengine/src/test/resources/helium/vis2/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "vis2",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "vis2",
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"zeppelin-tabledata": "*",
|
||||
"zeppelin-vis": "*"
|
||||
}
|
||||
}
|
||||
38
zeppelin-zengine/src/test/resources/helium/vis2/vis2.js
Normal file
38
zeppelin-zengine/src/test/resources/helium/vis2/vis2.js
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue