diff --git a/LICENSE b/LICENSE index 83a913190a..e206a6c2eb 100644 --- a/LICENSE +++ b/LICENSE @@ -255,6 +255,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version (Apache 2.0) Bootstrap v3.0.2 (http://getbootstrap.com/) - https://github.com/twbs/bootstrap/blob/v3.0.2/LICENSE (Apache 2.0) Software under ./bigquery/* was developed at Google (http://www.google.com/). Licensed under the Apache v2.0 License. (Apache 2.0) Roboto Font (https://github.com/google/roboto/) + (Apache 2.0) Gson extra (https://github.com/DanySK/gson-extras) ======================================================================== BSD 3-Clause licenses diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala index 363da7b1d2..c83a186a9c 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala @@ -30,7 +30,7 @@ import com.datastax.driver.core.exceptions.DriverException import com.datastax.driver.core.policies.{LoggingRetryPolicy, FallthroughRetryPolicy, DowngradingConsistencyRetryPolicy, Policies} import org.apache.zeppelin.cassandra.TextBlockHierarchy._ import org.apache.zeppelin.display.AngularObjectRegistry -import org.apache.zeppelin.display.Input.ParamOption +import org.apache.zeppelin.display.ui.OptionInput.ParamOption import org.apache.zeppelin.interpreter.InterpreterResult.Code import org.apache.zeppelin.interpreter.{InterpreterException, InterpreterResult, InterpreterContext} import org.slf4j.LoggerFactory diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java index 698397ae94..f3848fd1a7 100644 --- a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java +++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java @@ -34,7 +34,7 @@ import com.datastax.driver.core.Statement; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.GUI; -import org.apache.zeppelin.display.Input.ParamOption; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterException; import org.junit.Rule; diff --git a/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java b/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java index e46065161b..7f6809a899 100644 --- a/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java +++ b/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java @@ -36,7 +36,7 @@ import org.apache.zeppelin.interpreter.InterpreterContextRunner; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.GUI; -import org.apache.zeppelin.display.Input.ParamOption; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.interpreter.RemoteWorksController; import org.apache.zeppelin.interpreter.InterpreterException; diff --git a/pom.xml b/pom.xml index e1a2094d44..3bede62938 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ 1.2.17 0.9.2 2.2 + 0.2.1 15.0 9.2.15.v20160210 4.3.3 @@ -192,6 +193,12 @@ ${gson.version} + + org.danilopianini + gson-extras + ${gson-extras.version} + + commons-configuration commons-configuration diff --git a/python/src/main/resources/python/zeppelin_python.py b/python/src/main/resources/python/zeppelin_python.py index 31b993dec2..eff88249ee 100644 --- a/python/src/main/resources/python/zeppelin_python.py +++ b/python/src/main/resources/python/zeppelin_python.py @@ -53,7 +53,7 @@ class PyZeppelinContext(object): def __init__(self, z): self.z = z - self.paramOption = gateway.jvm.org.apache.zeppelin.display.Input.ParamOption + self.paramOption = gateway.jvm.org.apache.zeppelin.display.ui.OptionInput.ParamOption self.javaList = gateway.jvm.java.util.ArrayList self.max_result = 1000 self._displayhook = lambda *args: None diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java index 6e96d9d2b4..b78410f9f1 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java @@ -40,7 +40,7 @@ import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.AngularObjectWatcher; import org.apache.zeppelin.display.GUI; -import org.apache.zeppelin.display.Input.ParamOption; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterContextRunner; import org.apache.zeppelin.interpreter.InterpreterException; @@ -114,14 +114,33 @@ public class ZeppelinContext { public SQLContext sqlContext; private GUI gui; + /** + * @deprecated use z.textbox instead + * + */ + @Deprecated @ZeppelinApi public Object input(String name) { - return input(name, ""); + return textbox(name); + } + + /** + * @deprecated use z.textbox instead + */ + @Deprecated + @ZeppelinApi + public Object input(String name, Object defaultValue) { + return textbox(name, defaultValue.toString()); } @ZeppelinApi - public Object input(String name, Object defaultValue) { - return gui.input(name, defaultValue); + public Object textbox(String name) { + return textbox(name, ""); + } + + @ZeppelinApi + public Object textbox(String name, String defaultValue) { + return gui.textbox(name, defaultValue); } @ZeppelinApi @@ -136,7 +155,7 @@ public class ZeppelinContext { } @ZeppelinApi - public scala.collection.Iterable checkbox(String name, + public scala.collection.Seq checkbox(String name, scala.collection.Iterable> options) { List allChecked = new LinkedList<>(); for (Tuple2 option : asJavaIterable(options)) { @@ -146,11 +165,12 @@ public class ZeppelinContext { } @ZeppelinApi - public scala.collection.Iterable checkbox(String name, + public scala.collection.Seq checkbox(String name, scala.collection.Iterable defaultChecked, scala.collection.Iterable> options) { - return collectionAsScalaIterable(gui.checkbox(name, asJavaCollection(defaultChecked), - tuplesToParamOptions(options))); + return scala.collection.JavaConversions.asScalaBuffer( + gui.checkbox(name, asJavaCollection(defaultChecked), + tuplesToParamOptions(options))).toSeq(); } private ParamOption[] tuplesToParamOptions( @@ -311,7 +331,7 @@ public class ZeppelinContext { */ @ZeppelinApi public void run(String noteId, String paragraphId) { - run(noteId, paragraphId, interpreterContext); + run(noteId, paragraphId, interpreterContext, true); } /** @@ -320,8 +340,27 @@ public class ZeppelinContext { */ @ZeppelinApi public void run(String paragraphId) { + run(paragraphId, true); + } + + /** + * Run paragraph by id + * @param paragraphId + * @param checkCurrentParagraph + */ + @ZeppelinApi + public void run(String paragraphId, boolean checkCurrentParagraph) { String noteId = interpreterContext.getNoteId(); - run(noteId, paragraphId, interpreterContext); + run(noteId, paragraphId, interpreterContext, checkCurrentParagraph); + } + + /** + * Run paragraph by id + * @param noteId + */ + @ZeppelinApi + public void run(String noteId, String paragraphId, InterpreterContext context) { + run(noteId, paragraphId, context, true); } /** @@ -330,8 +369,9 @@ public class ZeppelinContext { * @param context */ @ZeppelinApi - public void run(String noteId, String paragraphId, InterpreterContext context) { - if (paragraphId.equals(context.getParagraphId())) { + public void run(String noteId, String paragraphId, InterpreterContext context, + boolean checkCurrentParagraph) { + if (paragraphId.equals(context.getParagraphId()) && checkCurrentParagraph) { throw new InterpreterException("Can not run current Paragraph"); } @@ -411,24 +451,50 @@ public class ZeppelinContext { */ @ZeppelinApi public void run(int idx) { + run(idx, true); + } + + /** + * + * @param idx paragraph index + * @param checkCurrentParagraph check whether you call this run method in the current paragraph. + * Set it to false only when you are sure you are not invoking this method to run current + * paragraph. Otherwise you would run current paragraph in infinite loop. + */ + public void run(int idx, boolean checkCurrentParagraph) { String noteId = interpreterContext.getNoteId(); - run(noteId, idx, interpreterContext); + run(noteId, idx, interpreterContext, checkCurrentParagraph); } /** * Run paragraph at index + * @param noteId * @param idx index starting from 0 * @param context interpreter context */ public void run(String noteId, int idx, InterpreterContext context) { + run(noteId, idx, context, true); + } + + /** + * + * @param noteId + * @param idx paragraph index + * @param context interpreter context + * @param checkCurrentParagraph check whether you call this run method in the current paragraph. + * Set it to false only when you are sure you are not invoking this method to run current + * paragraph. Otherwise you would run current paragraph in infinite loop. + */ + public void run(String noteId, int idx, InterpreterContext context, + boolean checkCurrentParagraph) { List runners = getInterpreterContextRunner(noteId, context); if (idx >= runners.size()) { throw new InterpreterException("Index out of bound"); } InterpreterContextRunner runner = runners.get(idx); - if (runner.getParagraphId().equals(context.getParagraphId())) { - throw new InterpreterException("Can not run current Paragraph"); + if (runner.getParagraphId().equals(context.getParagraphId()) && checkCurrentParagraph) { + throw new InterpreterException("Can not run current Paragraph: " + runner.getParagraphId()); } runner.run(); diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 5029d59c27..da4d7943e7 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -97,13 +97,15 @@ class PyZeppelinContext(dict): def checkbox(self, name, options, defaultChecked=None): if defaultChecked is None: - defaultChecked = list(map(lambda items: items[0], options)) + defaultChecked = [] optionTuples = list(map(lambda items: self.__tupleToScalaTuple2(items), options)) optionIterables = gateway.jvm.scala.collection.JavaConversions.collectionAsScalaIterable(optionTuples) defaultCheckedIterables = gateway.jvm.scala.collection.JavaConversions.collectionAsScalaIterable(defaultChecked) - - checkedIterables = self.z.checkbox(name, defaultCheckedIterables, optionIterables) - return gateway.jvm.scala.collection.JavaConversions.asJavaCollection(checkedIterables) + checkedItems = gateway.jvm.scala.collection.JavaConversions.seqAsJavaList(self.z.checkbox(name, defaultCheckedIterables, optionIterables)) + result = [] + for checkedItem in checkedItems: + result.append(checkedItem) + return result; def registerHook(self, event, cmd, replName=None): if replName is None: diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index e55144cebb..109099cfc7 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -61,6 +61,11 @@ gson + + org.danilopianini + gson-extras + + org.apache.commons commons-exec diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java index 929533bd1e..84ee67e7ff 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java @@ -34,6 +34,8 @@ import org.slf4j.LoggerFactory; * @param */ public class AngularObject { + private static final Logger LOGGER = LoggerFactory.getLogger(AngularObject.class); + private String name; private T object; @@ -172,7 +174,7 @@ public class AngularObject { if (emit) { emit(); } - + LOGGER.debug("Update angular object: " + name + " with value: " + o); final Logger logger = LoggerFactory.getLogger(AngularObject.class); List ws = new LinkedList<>(); synchronized (watchers) { diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java index 30a8ba7799..66b21c62ef 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java @@ -17,17 +17,27 @@ package org.apache.zeppelin.display; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.zeppelin.display.ui.CheckBox; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; +import org.apache.zeppelin.display.ui.Select; +import org.apache.zeppelin.display.ui.TextBox; + import java.io.Serializable; import java.util.*; -import org.apache.zeppelin.display.Input.ParamOption; /** * Settings of a form. */ public class GUI implements Serializable { + private static Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(Input.TypeAdapterFactory) + .create(); + Map params = new HashMap<>(); // form parameters from client LinkedHashMap forms = new LinkedHashMap<>(); // form configuration @@ -51,19 +61,29 @@ public class GUI implements Serializable { this.forms = forms; } + @Deprecated + public Object input(String id) { + return textbox(id, ""); + } + + @Deprecated public Object input(String id, Object defaultValue) { + return textbox(id, defaultValue.toString()); + } + + public Object textbox(String id, String defaultValue) { // first find values from client and then use default Object value = params.get(id); if (value == null) { value = defaultValue; } - forms.put(id, new Input(id, defaultValue, "input")); + forms.put(id, new TextBox(id, defaultValue)); return value; } - public Object input(String id) { - return input(id, ""); + public Object textbox(String id) { + return textbox(id, ""); } public Object select(String id, Object defaultValue, ParamOption[] options) { @@ -71,18 +91,18 @@ public class GUI implements Serializable { if (value == null) { value = defaultValue; } - forms.put(id, new Input(id, defaultValue, "select", options)); + forms.put(id, new Select(id, defaultValue, options)); return value; } - public Collection checkbox(String id, Collection defaultChecked, + public List checkbox(String id, Collection defaultChecked, ParamOption[] options) { Collection checked = (Collection) params.get(id); if (checked == null) { checked = defaultChecked; } - forms.put(id, new Input(id, defaultChecked, "checkbox", options)); - Collection filtered = new LinkedList<>(); + forms.put(id, new CheckBox(id, defaultChecked, options)); + List filtered = new LinkedList<>(); for (Object o : checked) { if (isValidOption(o, options)) { filtered.add(o); @@ -103,4 +123,41 @@ public class GUI implements Serializable { public void clear() { this.forms = new LinkedHashMap<>(); } + + public String toJson() { + return gson.toJson(this); + } + + public void convertOldInput() { + for (Map.Entry entry : forms.entrySet()) { + if (entry.getValue() instanceof OldInput) { + Input convertedInput = convertFromOldInput((OldInput) entry.getValue()); + forms.put(entry.getKey(), convertedInput); + } + } + } + + public static GUI fromJson(String json) { + GUI gui = gson.fromJson(json, GUI.class); + gui.convertOldInput(); + return gui; + } + + private Input convertFromOldInput(OldInput oldInput) { + Input convertedInput = null; + + if (oldInput.options == null || oldInput instanceof OldInput.OldTextBox) { + convertedInput = new TextBox(oldInput.name, oldInput.defaultValue.toString()); + } else if (oldInput instanceof OldInput.OldCheckBox) { + convertedInput = new CheckBox(oldInput.name, (List) oldInput.defaultValue, oldInput.options); + } else if (oldInput instanceof OldInput && oldInput.options != null) { + convertedInput = new Select(oldInput.name, oldInput.defaultValue, oldInput.options); + } else { + throw new RuntimeException("Can not convert this OldInput."); + } + convertedInput.setDisplayName(oldInput.getDisplayName()); + convertedInput.setHidden(oldInput.isHidden()); + convertedInput.setArgument(oldInput.getArgument()); + return convertedInput; + } } diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java index 4924b2ba71..12fa7820d6 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java @@ -18,6 +18,8 @@ package org.apache.zeppelin.display; import org.apache.commons.lang.StringUtils; +import org.apache.zeppelin.display.ui.*; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; import java.io.Serializable; import java.util.*; @@ -25,105 +27,43 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Input type. + * Base class for dynamic forms. Also used as factory class of dynamic forms. + * + * @param */ -public class Input implements Serializable { - /** - * Parameters option. - */ - public static class ParamOption { - Object value; - String displayName; +public class Input implements Serializable { - public ParamOption(Object value, String displayName) { - super(); - this.value = value; - this.displayName = displayName; - } + // @TODO(zjffdu). Use gson's RuntimeTypeAdapterFactory and remove the old input form support + // in future. + public static final RuntimeTypeAdapterFactory TypeAdapterFactory = + RuntimeTypeAdapterFactory.of(Input.class, "type") + .registerSubtype(TextBox.class, "TextBox") + .registerSubtype(Select.class, "Select") + .registerSubtype(CheckBox.class, "CheckBox") + .registerSubtype(OldInput.OldTextBox.class, "input") + .registerSubtype(OldInput.OldSelect.class, "select") + .registerSubtype(OldInput.OldCheckBox.class, "checkbox") + .registerSubtype(OldInput.class, null); - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ParamOption that = (ParamOption) o; - - if (value != null ? !value.equals(that.value) : that.value != null) return false; - return displayName != null ? displayName.equals(that.displayName) : that.displayName == null; - - } - - @Override - public int hashCode() { - int result = value != null ? value.hashCode() : 0; - result = 31 * result + (displayName != null ? displayName.hashCode() : 0); - return result; - } - - public Object getValue() { - return value; - } - - public void setValue(Object value) { - this.value = value; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } + protected String name; + protected String displayName; + protected T defaultValue; + protected boolean hidden; + protected String argument; + public Input() { } - String name; - String displayName; - String type; - String argument; - Object defaultValue; - ParamOption[] options; - boolean hidden; - - public Input(String name, Object defaultValue, String type) { - this.name = name; - this.displayName = name; - this.defaultValue = defaultValue; - this.type = type; - } - - public Input(String name, Object defaultValue, String type, ParamOption[] options) { - this.name = name; - this.displayName = name; - this.defaultValue = defaultValue; - this.type = type; - this.options = options; - } - - public Input(String name, String displayName, String type, String argument, Object defaultValue, - ParamOption[] options, boolean hidden) { - super(); - this.name = name; - this.displayName = displayName; - this.argument = argument; - this.type = type; - this.defaultValue = defaultValue; - this.options = options; - this.hidden = hidden; - } - - @Override - public boolean equals(Object o) { - return name.equals(((Input) o).getName()); + public boolean isHidden() { + return hidden; } public String getName() { - return name; + return this.name; } - public void setName(String name) { - this.name = name; + public T getDefaultValue() { + return defaultValue; } public String getDisplayName() { @@ -134,41 +74,37 @@ public class Input implements Serializable { this.displayName = displayName; } - public String getType() { - return type; + public void setArgument(String argument) { + this.argument = argument; } - public void setType(String type) { - this.type = type; + public void setHidden(boolean hidden) { + this.hidden = hidden; } - public Object getDefaultValue() { - return defaultValue; + public String getArgument() { + return argument; } - public void setDefaultValue(Object defaultValue) { - this.defaultValue = defaultValue; + public static TextBox textbox(String name, String defaultValue) { + return new TextBox(name, defaultValue); } - public ParamOption[] getOptions() { - return options; + public static Select select(String name, Object defaultValue, ParamOption[] options) { + return new Select(name, defaultValue, options); } - public void setOptions(ParamOption[] options) { - this.options = options; - } - - public boolean isHidden() { - return hidden; + public static CheckBox checkbox(String name, Object[] defaultChecked, ParamOption[] options) { + return new CheckBox(name, defaultChecked, options); } // Syntax of variables: ${TYPE:NAME=DEFAULT_VALUE1|DEFAULT_VALUE2|...,VALUE1|VALUE2|...} // Type is optional. Type may contain an optional argument with syntax: TYPE(ARG) // NAME and VALUEs may contain an optional display name with syntax: NAME(DISPLAY_NAME) // DEFAULT_VALUEs may not contain display name - // Examples: ${age} input form without default value - // ${age=3} input form with default value - // ${age(Age)=3} input form with display name and default value + // Examples: ${age} textbox form without default value + // ${age=3} textbox form with default value + // ${age(Age)=3} textbox form with display name and default value // ${country=US(United States)|UK|JP} select form with // ${checkbox( or ):country(Country)=US|JP,US(United States)|UK|JP} // checkbox form with " or " as delimiter: will be @@ -282,7 +218,22 @@ public class Input implements Serializable { } - return new Input(varName, displayName, type, arg, defaultValue, paramOptions, hidden); + Input input = null; + if (type == null) { + if (paramOptions == null) { + input = new TextBox(varName, (String) defaultValue); + } else { + input = new Select(varName, defaultValue, paramOptions); + } + } else if (type.equals("checkbox")) { + input = new CheckBox(varName, (Object[]) defaultValue, paramOptions); + } else { + throw new RuntimeException("Could not recognize dynamic form with type: " + type); + } + input.setArgument(arg); + input.setDisplayName(displayName); + input.setHidden(hidden); + return input; } public static LinkedHashMap extractSimpleQueryForm(String script) { @@ -314,11 +265,12 @@ public class Input implements Serializable { if (params.containsKey(input.name)) { value = params.get(input.name); } else { - value = input.defaultValue; + value = input.getDefaultValue(); } String expanded; if (value instanceof Object[] || value instanceof Collection) { // multi-selection + OptionInput optionInput = (OptionInput) input; String delimiter = input.argument; if (delimiter == null) { delimiter = DEFAULT_DELIMITER; @@ -327,7 +279,7 @@ public class Input implements Serializable { : Arrays.asList((Object[]) value); List validChecked = new LinkedList<>(); for (Object o : checked) { // filter out obsolete checked values - for (ParamOption option : input.getOptions()) { + for (ParamOption option : optionInput.getOptions()) { if (option.getValue().equals(o)) { validChecked.add(o); break; diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java new file mode 100644 index 0000000000..7c67dad834 --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java @@ -0,0 +1,87 @@ +/* + * 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.display; + +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; + +/** + * Old Input type. + * The reason I still keep Old Input is for compatibility. There's one bug in the old input forms. + * There's 2 ways to create input forms: frontend & backend. + * The bug is in frontend. The type would not be set correctly when input form + * is created in frontend (Input.getInputForm). + */ +public class OldInput extends Input { + + ParamOption[] options; + + public OldInput() {} + + public OldInput(String name, Object defaultValue) { + this.name = name; + this.displayName = name; + this.defaultValue = defaultValue; + } + + public OldInput(String name, Object defaultValue, ParamOption[] options) { + this.name = name; + this.displayName = name; + this.defaultValue = defaultValue; + this.options = options; + } + + @Override + public boolean equals(Object o) { + return name.equals(((OldInput) o).getName()); + } + + public ParamOption[] getOptions() { + return options; + } + + public void setOptions(ParamOption[] options) { + this.options = options; + } + + /** + * + */ + public static class OldTextBox extends OldInput { + public OldTextBox(String name, Object defaultValue) { + super(name, defaultValue); + } + } + + /** + * + */ + public static class OldSelect extends OldInput { + public OldSelect(String name, Object defaultValue, ParamOption[] options) { + super(name, defaultValue, options); + } + } + + /** + * + */ + public static class OldCheckBox extends OldInput { + public OldCheckBox(String name, Object defaultValue, ParamOption[] options) { + super(name, defaultValue, options); + } + } +} diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java new file mode 100644 index 0000000000..da05caa633 --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java @@ -0,0 +1,149 @@ +/* + * 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.display; + +import com.google.gson.*; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Copied from gson with minor changes to support old input forms + * + * @param + */ +public class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + private final Class baseType; + private final String typeFieldName; + private final Map> labelToSubtype = new LinkedHashMap>(); + private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); + + private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName) { + if (typeFieldName == null || baseType == null) { + throw new NullPointerException(); + } + this.baseType = baseType; + this.typeFieldName = typeFieldName; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { + return new RuntimeTypeAdapterFactory(baseType, typeFieldName); + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeTypeAdapterFactory of(Class baseType) { + return new RuntimeTypeAdapterFactory(baseType, "type"); + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + if (type == null) { + throw new NullPointerException(); + } + if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { + throw new IllegalArgumentException("types and labels must be unique"); + } + labelToSubtype.put(label, type); + subtypeToLabel.put(type, label); + return this; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type) { + return registerSubtype(type, type.getSimpleName()); + } + + public TypeAdapter create(Gson gson, TypeToken type) { + if (type.getRawType() != baseType) { + return null; + } + + final Map> labelToDelegate = new LinkedHashMap>(); + final Map, TypeAdapter> subtypeToDelegate = + new LinkedHashMap, TypeAdapter>(); + for (Map.Entry> entry : labelToSubtype.entrySet()) { + TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); + labelToDelegate.put(entry.getKey(), delegate); + subtypeToDelegate.put(entry.getValue(), delegate); + } + + return new TypeAdapter() { + @Override public R read(JsonReader in) throws IOException { + JsonElement jsonElement = Streams.parse(in); + JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + String label = (labelJsonElement == null ? null : labelJsonElement.getAsString()); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + + label + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } + + @Override public void write(JsonWriter out, R value) throws IOException { + Class srcType = value.getClass(); + String label = subtypeToLabel.get(srcType); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); + if (delegate == null) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + "; did you forget to register a subtype?"); + } + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + if (jsonObject.has(typeFieldName) && !srcType.getSimpleName().equals("OldInput")) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + " because it already defines a field named " + typeFieldName); + } + JsonObject clone = new JsonObject(); + if (!srcType.getSimpleName().equals("OldInput")) { + clone.add(typeFieldName, new JsonPrimitive(label)); + } + for (Map.Entry e : jsonObject.entrySet()) { + clone.add(e.getKey(), e.getValue()); + } + Streams.write(clone, out); + } + }.nullSafe(); + } +} + diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java new file mode 100644 index 0000000000..f9b4650f4d --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java @@ -0,0 +1,45 @@ +/* + * 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.display.ui; + +import java.awt.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +/** + * Html Checkbox + */ +public class CheckBox extends OptionInput { + + public CheckBox() { + } + + public CheckBox(String name, Object[] defaultValue, ParamOption[] options) { + this.name = name; + this.displayName = name; + this.defaultValue = defaultValue; + this.options = options; + } + + public CheckBox(String name, Collection defaultValue, ParamOption[] options) { + this(name, defaultValue.toArray(), options); + } + +} diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java new file mode 100644 index 0000000000..d5a1c0d2c1 --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java @@ -0,0 +1,85 @@ +/* + * 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.display.ui; + +import org.apache.zeppelin.display.Input; + +/** + * Base class for Input with options + * + * @param + */ +public abstract class OptionInput extends Input { + + /** + * Parameters option. + */ + public static class ParamOption { + Object value; + String displayName; + + public ParamOption(Object value, String displayName) { + super(); + this.value = value; + this.displayName = displayName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ParamOption that = (ParamOption) o; + + if (value != null ? !value.equals(that.value) : that.value != null) return false; + return displayName != null ? displayName.equals(that.displayName) : that.displayName == null; + + } + + @Override + public int hashCode() { + int result = value != null ? value.hashCode() : 0; + result = 31 * result + (displayName != null ? displayName.hashCode() : 0); + return result; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + } + + protected ParamOption[] options; + + public ParamOption[] getOptions() { + return options; + } +} diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java new file mode 100644 index 0000000000..212d3d747d --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java @@ -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. + */ + +package org.apache.zeppelin.display.ui; + +/** + * Html Dropdown list + */ +public class Select extends OptionInput { + + public Select() { + + } + + public Select(String name, Object defaultValue, ParamOption[] options) { + this.name = name; + this.displayName = name; + this.defaultValue = defaultValue; + this.options = options; + } + +} diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java new file mode 100644 index 0000000000..b9f994646d --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java @@ -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. + */ + + +package org.apache.zeppelin.display.ui; + +import org.apache.zeppelin.display.Input; + +/** + * Html TextBox control + */ +public class TextBox extends Input { + + public TextBox() { + + } + + public TextBox(String name, String defaultValue) { + this.name = name; + this.displayName = name; + this.defaultValue = defaultValue; + } + +} diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java index bb09d19b1f..f1cbef8339 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java @@ -92,7 +92,12 @@ public class LazyOpenInterpreter @Override public InterpreterResult interpret(String st, InterpreterContext context) { open(); - return intp.interpret(st, context); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + return intp.interpret(st, context); + } finally { + Thread.currentThread().setContextClassLoader(classLoader); + } } @Override diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java index 2f9d2bbba5..123ad75741 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java @@ -381,14 +381,14 @@ public class RemoteInterpreter extends Interpreter { context.getConfig().putAll(remoteConfig); if (form == FormType.NATIVE) { - GUI remoteGui = gson.fromJson(remoteResult.getGui(), GUI.class); + GUI remoteGui = GUI.fromJson(remoteResult.getGui()); currentGUI.clear(); currentGUI.setParams(remoteGui.getParams()); currentGUI.setForms(remoteGui.getForms()); } else if (form == FormType.SIMPLE) { final Map currentForms = currentGUI.getForms(); final Map currentParams = currentGUI.getParams(); - final GUI remoteGUI = gson.fromJson(remoteResult.getGui(), GUI.class); + final GUI remoteGUI = GUI.fromJson(remoteResult.getGui()); final Map remoteForms = remoteGUI.getForms(); final Map remoteParams = remoteGUI.getParams(); currentForms.putAll(remoteForms); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 3b7ec5caa7..6c43813d93 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -592,7 +592,7 @@ public class RemoteInterpreterServer gson.fromJson(ric.getAuthenticationInfo(), AuthenticationInfo.class), (Map) gson.fromJson(ric.getConfig(), new TypeToken>() {}.getType()), - gson.fromJson(ric.getGui(), GUI.class), + GUI.fromJson(ric.getGui()), interpreterGroup.getAngularObjectRegistry(), interpreterGroup.getResourcePool(), contextRunners, output, remoteWorksController, eventClient); @@ -737,7 +737,7 @@ public class RemoteInterpreterServer result.code().name(), msg, gson.toJson(config), - gson.toJson(gui)); + gui.toJson()); } @Override diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java new file mode 100644 index 0000000000..6def2e7445 --- /dev/null +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java @@ -0,0 +1,120 @@ +/* + * 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.display; + +import org.apache.commons.io.IOUtils; +import org.apache.zeppelin.display.ui.CheckBox; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; +import org.apache.zeppelin.display.ui.Select; +import org.apache.zeppelin.display.ui.TextBox; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class GUITest { + + private ParamOption[] options = new ParamOption[]{ + new ParamOption("1", "value_1"), + new ParamOption("2", "value_2") + }; + + private List checkedItems; + + @Before + public void setUp() { + checkedItems = new ArrayList<>(); + checkedItems.add("1"); + } + + @Test + public void testGson() { + GUI gui = new GUI(); + gui.textbox("textbox_1", "default_text_1"); + gui.select("select_1", "1", options); + List list = new ArrayList(); + list.add("1"); + gui.checkbox("checkbox_1", list, options); + + String json = gui.toJson(); + System.out.println(json); + GUI gui2 = GUI.fromJson(json); + assertEquals(gui2.toJson(), json); + assertEquals(gui2.forms, gui2.forms); + assertEquals(gui2.params, gui2.params); + } + + // Case 1. Old input forms are created in backend, in this case type is always set + @Test + public void testOldGson_1() throws IOException { + + GUI gui = new GUI(); + gui.forms.put("textbox_1", new OldInput.OldTextBox("textbox_1", "default_text_1")); + gui.forms.put("select_1", new OldInput.OldSelect("select_1", "1", options)); + gui.forms.put("checkbox_1", + new OldInput.OldCheckBox("checkbox_1", checkedItems, options)); + + // convert to old json format. + String json = gui.toJson(); + + // convert to new input forms + GUI gui2 = GUI.fromJson(json); + assertTrue(3 == gui2.forms.size()); + assertTrue(gui2.forms.get("textbox_1") instanceof TextBox); + assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue()); + assertTrue(gui2.forms.get("select_1") instanceof Select); + assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions()); + assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox); + assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions()); + } + + // Case 2. Old input forms are created in frontend, in this case type is only set for checkbox + // Actually this is a bug due to method Input#getInputForm + @Test + public void testOldGson_2() throws IOException { + + GUI gui = new GUI(); + gui.forms.put("textbox_1", new OldInput("textbox_1", "default_text_1")); + gui.forms.put("select_1", new OldInput("select_1", "1", options)); + gui.forms.put("checkbox_1", + new OldInput.OldCheckBox("checkbox_1", checkedItems, options)); + + // convert to old json format. + String json = gui.toJson(); + + // convert to new input forms + GUI gui2 = GUI.fromJson(json); + assertTrue(3 == gui2.forms.size()); + assertTrue(gui2.forms.get("textbox_1") instanceof TextBox); + assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue()); + assertTrue(gui2.forms.get("select_1") instanceof Select); + assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions()); + assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox); + assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions()); + } +} diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java index b6f1e3e783..d15fab4a57 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java @@ -20,16 +20,19 @@ package org.apache.zeppelin.display; import java.util.HashMap; import java.util.Map; +import org.apache.zeppelin.display.ui.CheckBox; +import org.apache.zeppelin.display.ui.OptionInput.ParamOption; +import org.apache.zeppelin.display.ui.Select; +import org.apache.zeppelin.display.ui.TextBox; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNull; -import org.apache.zeppelin.display.Input.ParamOption; - public class InputTest { @Before @@ -42,7 +45,7 @@ public class InputTest { @Test public void testFormExtraction() { - // input form + // textbox form String script = "${input_form=}"; Map forms = Input.extractSimpleQueryForm(script); assertEquals(1, forms.size()); @@ -50,50 +53,57 @@ public class InputTest { assertEquals("input_form", form.name); assertNull(form.displayName); assertEquals("", form.defaultValue); - assertNull(form.options); + assertTrue(form instanceof TextBox); - // input form with display name & default value + // textbox form with display name & default value script = "${input_form(Input Form)=xxx}"; forms = Input.extractSimpleQueryForm(script); form = forms.get("input_form"); assertEquals("xxx", form.defaultValue); + assertTrue(form instanceof TextBox); // selection form script = "${select_form(Selection Form)=op1,op1|op2(Option 2)|op3}"; form = Input.extractSimpleQueryForm(script).get("select_form"); assertEquals("select_form", form.name); assertEquals("op1", form.defaultValue); + assertTrue(form instanceof Select); assertArrayEquals(new ParamOption[]{new ParamOption("op1", null), - new ParamOption("op2", "Option 2"), new ParamOption("op3", null)}, form.options); + new ParamOption("op2", "Option 2"), new ParamOption("op3", null)}, + ((Select) form).getOptions()); // checkbox form script = "${checkbox:checkbox_form=op1,op1|op2|op3}"; form = Input.extractSimpleQueryForm(script).get("checkbox_form"); assertEquals("checkbox_form", form.name); - assertEquals("checkbox", form.type); + assertTrue(form instanceof CheckBox); + assertArrayEquals(new Object[]{"op1"}, (Object[]) form.defaultValue); assertArrayEquals(new ParamOption[]{new ParamOption("op1", null), - new ParamOption("op2", null), new ParamOption("op3", null)}, form.options); + new ParamOption("op2", null), new ParamOption("op3", null)}, + ((CheckBox) form).getOptions()); // checkbox form with multiple default checks script = "${checkbox:checkbox_form(Checkbox Form)=op1|op3,op1(Option 1)|op2|op3}"; form = Input.extractSimpleQueryForm(script).get("checkbox_form"); assertEquals("checkbox_form", form.name); assertEquals("Checkbox Form", form.displayName); - assertEquals("checkbox", form.type); + assertTrue(form instanceof CheckBox); assertArrayEquals(new Object[]{"op1", "op3"}, (Object[]) form.defaultValue); assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"), - new ParamOption("op2", null), new ParamOption("op3", null)}, form.options); + new ParamOption("op2", null), new ParamOption("op3", null)}, + ((CheckBox) form).getOptions()); // checkbox form with no default check script = "${checkbox:checkbox_form(Checkbox Form)=,op1(Option 1)|op2(Option 2)|op3(Option 3)}"; form = Input.extractSimpleQueryForm(script).get("checkbox_form"); assertEquals("checkbox_form", form.name); assertEquals("Checkbox Form", form.displayName); - assertEquals("checkbox", form.type); + assertTrue(form instanceof CheckBox); assertArrayEquals(new Object[]{}, (Object[]) form.defaultValue); assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"), - new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")}, form.options); + new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")}, + ((CheckBox) form).getOptions()); } @@ -125,4 +135,5 @@ public class InputTest { assertEquals("INPUT=some_inputSELECTED=s_op2\nCHECKED=c_op1\n" + "NEW_CHECKED=nc_a and nc_c", replaced); } + } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java index bc15bfe485..8ca04762d3 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java @@ -341,11 +341,11 @@ public class NotebookRestApi { List initialParagraphs = request.getParagraphs(); if (initialParagraphs != null) { for (NewParagraphRequest paragraphRequest : initialParagraphs) { - Paragraph p = note.addParagraph(subject); + Paragraph p = note.addNewParagraph(subject); initParagraph(p, paragraphRequest, user); } } - note.addParagraph(subject); // add one paragraph to the last + note.addNewParagraph(subject); // add one paragraph to the last String noteName = request.getName(); if (noteName.isEmpty()) { noteName = "Note " + note.getId(); @@ -433,9 +433,9 @@ public class NotebookRestApi { Paragraph p; Double indexDouble = request.getIndex(); if (indexDouble == null) { - p = note.addParagraph(subject); + p = note.addNewParagraph(subject); } else { - p = note.insertParagraph(indexDouble.intValue(), subject); + p = note.insertNewParagraph(indexDouble.intValue(), subject); } initParagraph(p, request, user); note.persist(subject); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java index 23d80a41c7..a9f2995214 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java @@ -37,7 +37,6 @@ import javax.servlet.http.HttpServletRequest; import com.google.common.base.Strings; import com.google.common.collect.Sets; -import com.google.gson.*; import org.apache.commons.lang.StringUtils; import org.apache.commons.vfs2.FileSystemException; import org.apache.zeppelin.conf.ZeppelinConfiguration; @@ -45,6 +44,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.AngularObjectRegistryListener; +import org.apache.zeppelin.display.Input; import org.apache.zeppelin.helium.ApplicationEventListener; import org.apache.zeppelin.helium.HeliumPackage; import org.apache.zeppelin.interpreter.Interpreter; @@ -134,7 +134,9 @@ public class NotebookServer extends WebSocketServlet } } } - }).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create(); + }).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .registerTypeAdapterFactory(Input.TypeAdapterFactory).create(); + final Map> noteSocketMap = new HashMap<>(); final Queue connectedSockets = new ConcurrentLinkedQueue<>(); final Map> userConnectedSockets = new ConcurrentHashMap<>(); @@ -183,10 +185,11 @@ public class NotebookServer extends WebSocketServlet Notebook notebook = notebook(); try { Message messagereceived = deserializeMessage(msg); - LOG.debug("RECEIVE << " + messagereceived.op); - LOG.debug("RECEIVE PRINCIPAL << " + messagereceived.principal); - LOG.debug("RECEIVE TICKET << " + messagereceived.ticket); - LOG.debug("RECEIVE ROLES << " + messagereceived.roles); + LOG.debug("RECEIVE << " + messagereceived.op + + ", RECEIVE PRINCIPAL << " + messagereceived.principal + + ", RECEIVE TICKET << " + messagereceived.ticket + + ", RECEIVE ROLES << " + messagereceived.roles + + ", RECEIVE DATA << " + messagereceived.data); if (LOG.isTraceEnabled()) { LOG.trace("RECEIVE MSG = " + messagereceived); @@ -643,6 +646,22 @@ public class NotebookServer extends WebSocketServlet broadcast(noteId, new Message(OP.INTERPRETER_BINDINGS).put("interpreterBindings", settingList)); } + public void unicastParagraph(Note note, Paragraph p, String user) { + if (!note.isPersonalizedMode() || p == null || user == null) { + return; + } + + if (!userConnectedSockets.containsKey(user)) { + LOG.warn("Failed to send unicast. user {} that is not in connections map", user); + return; + } + + for (NotebookSocket conn : userConnectedSockets.get(user)) { + Message m = new Message(OP.PARAGRAPH).put("paragraph", p); + unicast(m, conn); + } + } + public void broadcastParagraph(Note note, Paragraph p) { if (note.isPersonalizedMode()) { broadcastParagraphs(p.getUserParagraphMap(), p); @@ -1004,7 +1023,7 @@ public class NotebookServer extends WebSocketServlet note = notebook.createNote(subject); } - note.addParagraph(subject); // it's an empty note. so add one paragraph + note.addNewParagraph(subject); // it's an empty note. so add one paragraph if (message != null) { String noteName = (String) message.get("name"); if (StringUtils.isEmpty(noteName)) { @@ -1300,9 +1319,15 @@ public class NotebookServer extends WebSocketServlet } final Note note = notebook.getNote(noteId); - note.clearParagraphOutput(paragraphId); - Paragraph paragraph = note.getParagraph(paragraphId); - broadcastParagraph(note, paragraph); + if (note.isPersonalizedMode()) { + String user = fromMessage.principal; + Paragraph p = note.clearPersonalizedParagraphOutput(paragraphId, user); + unicastParagraph(note, p, user); + } else { + note.clearParagraphOutput(paragraphId); + Paragraph paragraph = note.getParagraph(paragraphId); + broadcastParagraph(note, paragraph); + } } private void completion(NotebookSocket conn, HashSet userAndRoles, Notebook notebook, @@ -1568,7 +1593,7 @@ public class NotebookServer extends WebSocketServlet return null; } - Paragraph newPara = note.insertParagraph(index, subject); + Paragraph newPara = note.insertNewParagraph(index, subject); note.persist(subject); broadcastNewParagraph(note, newPara); @@ -1695,12 +1720,21 @@ public class NotebookServer extends WebSocketServlet return; } + // 1. clear paragraph only if personalized, + // otherwise this will be handed in `onOutputClear` + final Note note = notebook.getNote(noteId); + if (note.isPersonalizedMode()) { + String user = fromMessage.principal; + Paragraph p = note.clearPersonalizedParagraphOutput(paragraphId, user); + unicastParagraph(note, p, user); + } + + // 2. set paragraph values String text = (String) fromMessage.get("paragraph"); String title = (String) fromMessage.get("title"); Map params = (Map) fromMessage.get("params"); Map config = (Map) fromMessage.get("config"); - final Note note = notebook.getNote(noteId); Paragraph p = setParagraphUsingMessage(note, fromMessage, paragraphId, text, title, params, config); @@ -1713,7 +1747,7 @@ public class NotebookServer extends WebSocketServlet if (!(p.getText().trim().equals(p.getMagic()) || Strings.isNullOrEmpty(p.getText())) && isTheLastParagraph) { - Paragraph newPara = note.addParagraph(p.getAuthenticationInfo()); + Paragraph newPara = note.addNewParagraph(p.getAuthenticationInfo()); broadcastNewParagraph(note, newPara); } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java index 8e09f00259..add23ac0fb 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java @@ -548,7 +548,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { try { createNewNote(); - setTextOfParagraph(1, "%spark println(\"Hello \"+z.input(\"name\", \"world\")) "); + setTextOfParagraph(1, "%spark println(\"Hello \"+z.textbox(\"name\", \"world\")) "); runParagraph(1); waitForParagraph(1, "FINISHED"); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java index 63c3ab1ffe..afdae10b66 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java @@ -137,7 +137,7 @@ public class ZeppelinIT extends AbstractZeppelinIT { * z.run(2, context) * } */ - setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2)})"); + setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2, false)})"); runParagraph(4); waitForParagraph(4, "FINISHED"); @@ -157,6 +157,21 @@ public class ZeppelinIT extends AbstractZeppelinIT { waitForText("myVar=3", By.xpath( getParagraphXPath(3) + "//div[contains(@id,\"_text\") and @class=\"text\"]")); + + /* + * Click element, again and see watcher still works + */ + driver.findElement(By.xpath( + getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).click(); + // check expected text + waitForText("BindingTest_4_", By.xpath( + getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")); + waitForParagraph(3, "FINISHED"); + + // check expected text by watcher + waitForText("myVar=4", By.xpath( + getParagraphXPath(3) + "//div[contains(@id,\"_text\") and @class=\"text\"]")); + /* * Unbind * z.angularUnbind("myVar") diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java index 84cdf66fa6..6849167da0 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java @@ -242,7 +242,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi { public void testInterpreterRestart() throws IOException, InterruptedException { // when: create new note Note note = ZeppelinServer.notebook.createNote(anonymous); - note.addParagraph(AuthenticationInfo.ANONYMOUS); + note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Paragraph p = note.getLastParagraph(); Map config = p.getConfig(); config.put("enabled", true); @@ -269,7 +269,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi { } // when: run markdown paragraph, again - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p.setConfig(config); p.setText("%md markdown restarted"); p.setAuthenticationInfo(anonymous); @@ -287,7 +287,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi { public void testRestartInterpreterPerNote() throws IOException, InterruptedException { // when: create new note Note note = ZeppelinServer.notebook.createNote(anonymous); - note.addParagraph(AuthenticationInfo.ANONYMOUS); + note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Paragraph p = note.getLastParagraph(); Map config = p.getConfig(); config.put("enabled", true); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java index c8b06d1487..ac8a27fd06 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java @@ -17,7 +17,6 @@ package org.apache.zeppelin.rest; -import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.commons.httpclient.methods.GetMethod; @@ -72,7 +71,7 @@ public class NotebookRestApiTest extends AbstractTestRestApi { @Test public void testGetNoteParagraphJobStatus() throws IOException { Note note1 = ZeppelinServer.notebook.createNote(anonymous); - note1.addParagraph(AuthenticationInfo.ANONYMOUS); + note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); String paragraphId = note1.getLastParagraph().getId(); @@ -94,9 +93,9 @@ public class NotebookRestApiTest extends AbstractTestRestApi { @Test public void testRunParagraphJob() throws IOException { Note note1 = ZeppelinServer.notebook.createNote(anonymous); - note1.addParagraph(AuthenticationInfo.ANONYMOUS); + note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); - Paragraph p = note1.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); // run blank paragraph PostMethod post = httpPost("/notebook/job/" + note1.getId() + "/" + p.getId(), ""); @@ -150,7 +149,7 @@ public class NotebookRestApiTest extends AbstractTestRestApi { public void testUpdateParagraphConfig() throws IOException { Note note = ZeppelinServer.notebook.createNote(anonymous); String noteId = note.getId(); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); assertNull(p.getConfig().get("colWidth")); String paragraphId = p.getId(); String jsonRequest = "{\"colWidth\": 6.0}"; @@ -176,11 +175,11 @@ public class NotebookRestApiTest extends AbstractTestRestApi { public void testClearAllParagraphOutput() throws IOException { // Create note and set result explicitly Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); InterpreterResult result = new InterpreterResult(InterpreterResult.Code.SUCCESS, InterpreterResult.Type.TEXT, "result"); p1.setResult(result); - Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p2.setReturn(result, new Throwable()); // clear paragraph result diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java index 3ae38e621d..4ee5a0078a 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java @@ -27,7 +27,6 @@ import com.google.common.collect.Sets; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.lang3.StringUtils; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.Paragraph; @@ -88,7 +87,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("note"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); paragraph.setConfig(config); @@ -228,7 +227,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("source note for export"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); paragraph.setConfig(config); @@ -261,7 +260,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName(noteName); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); paragraph.setConfig(config); @@ -323,7 +322,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("source note for clone"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); paragraph.setConfig(config); @@ -374,7 +373,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("note for run test"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); @@ -429,7 +428,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("note for run test"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); @@ -483,7 +482,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); assertNotNull("can't create new note", note); note.setName("note for run test"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = paragraph.getConfig(); config.put("enabled", true); @@ -527,7 +526,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); note.setName("note for run test"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); paragraph.setText("%md This is test paragraph."); Map config = paragraph.getConfig(); @@ -576,7 +575,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); note.setName("note for run test"); - Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)"); note.persist(anonymous); @@ -656,7 +655,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { public void testGetParagraph() throws IOException { Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p.setTitle("hello"); p.setText("world"); note.persist(anonymous); @@ -685,11 +684,11 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { public void testMoveParagraph() throws IOException { Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p.setTitle("title1"); p.setText("text1"); - Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p2.setTitle("title2"); p2.setText("text2"); @@ -717,7 +716,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { public void testDeleteParagraph() throws IOException { Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p.setTitle("title1"); p.setText("text1"); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index 9bcdbfa875..ec0e0bdb29 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -80,7 +80,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { public void scalaOutputTest() throws IOException { // create new note Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -106,7 +106,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { Note note = ZeppelinServer.notebook.createNote(anonymous); // run markdown paragraph, again - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -127,7 +127,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { // DataFrame API is available from spark 1.3 if (sparkVersion >= 13) { // test basic dataframe api - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -141,7 +141,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { "Array[org.apache.spark.sql.Row] = Array([hello,20])")); // test display DataFrame - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -156,7 +156,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { // test display DataSet if (sparkVersion >= 20) { - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -195,7 +195,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { if (sparkVersion >= 20) { sqlContextName = "spark"; } - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -222,7 +222,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { if (isPyspark() && sparkVersion >= 12) { // pyspark supported from 1.2.1 // run markdown paragraph, again - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -234,7 +234,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assertEquals("55\n", p.getResult().message().get(0).getData()); if (sparkVersion >= 13) { // run sqlContext test - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -248,7 +248,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData()); // test display Dataframe - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -264,7 +264,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assertEquals("age\tid\n20\t1\n", p.getResult().message().get(0).getData()); // test udf - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -278,7 +278,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { } if (sparkVersion >= 20) { // run SparkSession test - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -292,7 +292,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData()); // test udf - p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -319,7 +319,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { if (isPyspark() && sparkVersionNumber >= 14) { // auto_convert enabled from spark 1.4 // run markdown paragraph, again - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); @@ -344,19 +344,19 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { public void zRunTest() throws IOException { // create new note Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p0 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p0 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config0 = p0.getConfig(); config0.put("enabled", true); p0.setConfig(config0); p0.setText("%spark z.run(1)"); p0.setAuthenticationInfo(anonymous); - Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config1 = p1.getConfig(); config1.put("enabled", true); p1.setConfig(config1); p1.setText("%spark val a=10"); p1.setAuthenticationInfo(anonymous); - Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config2 = p2.getConfig(); config2.put("enabled", true); p2.setConfig(config2); @@ -374,7 +374,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assertEquals(Status.FINISHED, p2.getStatus()); assertEquals("10", p2.getResult().message().get(0).getData()); - Paragraph p3 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p3 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config3 = p3.getConfig(); config3.put("enabled", true); p3.setConfig(config3); @@ -424,7 +424,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { } // load dep - Paragraph p0 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p0 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p0.getConfig(); config.put("enabled", true); p0.setConfig(config); @@ -439,7 +439,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { FileUtils.write(tmpFile, "a,b\n1,2"); // load data using libraries from dep loader - Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); p1.setConfig(config); String sqlContextName = "sqlContext"; @@ -465,7 +465,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { * eg. 1.1.x => 11, 1.2.x => 12, 1.3.x => 13 ... */ private int getSparkVersionNumber(Note note) { - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); note.setName("note"); Map config = p.getConfig(); config.put("enabled", true); @@ -483,18 +483,19 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { } @Test - public void testZeppelinContextDynamicForms() throws IOException { + public void testSparkZeppelinContextDynamicForms() throws IOException { Note note = ZeppelinServer.notebook.createNote(anonymous); - Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); note.setName("note"); Map config = p.getConfig(); config.put("enabled", true); p.setConfig(config); - String code = "%spark.spark z.input(\"my_input\", \"default_name\")\n" + - "z.select(\"my_select\", \"select_2\"," + - "Seq((\"1\", \"select_1\"), (\"2\", \"select_2\")))\n" + - "z.checkbox(\"my_checkbox\", Seq(\"check_1\"), " + - "Seq((\"1\", \"check_1\"), (\"2\", \"check_2\")))"; + String code = "%spark.spark println(z.textbox(\"my_input\", \"default_name\"))\n" + + "println(z.select(\"my_select\", \"1\"," + + "Seq((\"1\", \"select_1\"), (\"2\", \"select_2\"))))\n" + + "val items=z.checkbox(\"my_checkbox\", Seq(\"2\"), " + + "Seq((\"1\", \"check_1\"), (\"2\", \"check_2\")))\n" + + "println(items(0))"; p.setText(code); p.setAuthenticationInfo(anonymous); note.run(p.getId()); @@ -505,5 +506,46 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi { assert(formIter.next().equals("my_input")); assert(formIter.next().equals("my_select")); assert(formIter.next().equals("my_checkbox")); + + // check dynamic forms values + String[] result = p.getResult().message().get(0).getData().split("\n"); + assertEquals(4, result.length); + assertEquals("default_name", result[0]); + assertEquals("1", result[1]); + assertEquals("items: Seq[Object] = Buffer(2)", result[2]); + assertEquals("2", result[3]); + } + + @Test + public void testPySparkZeppelinContextDynamicForms() throws IOException { + Note note = ZeppelinServer.notebook.createNote(anonymous); + Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); + note.setName("note"); + Map config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + String code = "%spark.pyspark print(z.input('my_input', 'default_name'))\n" + + "print(z.select('my_select', " + + "[('1', 'select_1'), ('2', 'select_2')], defaultValue='1'))\n" + + "items=z.checkbox('my_checkbox', " + + "[('1', 'check_1'), ('2', 'check_2')], defaultChecked=['2'])\n" + + "print(items[0])"; + p.setText(code); + p.setAuthenticationInfo(anonymous); + note.run(p.getId()); + waitForFinish(p); + + assertEquals(Status.FINISHED, p.getStatus()); + Iterator formIter = p.settings.getForms().keySet().iterator(); + assert(formIter.next().equals("my_input")); + assert(formIter.next().equals("my_select")); + assert(formIter.next().equals("my_checkbox")); + + // check dynamic forms values + String[] result = p.getResult().message().get(0).getData().split("\n"); + assertEquals(3, result.length); + assertEquals("default_name", result[0]); + assertEquals("1", result[1]); + assertEquals("2", result[2]); } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java index c339140419..f905227392 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java @@ -110,7 +110,7 @@ public class NotebookServerTest extends AbstractTestRestApi { } // start interpreter process - Paragraph p1 = note1.addParagraph(AuthenticationInfo.ANONYMOUS); + Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); p1.setText("%md start remote interpreter process"); p1.setAuthenticationInfo(anonymous); note1.run(p1.getId()); diff --git a/zeppelin-web/.eslintrc b/zeppelin-web/.eslintrc index 3981770704..1fe3fa5e7d 100644 --- a/zeppelin-web/.eslintrc +++ b/zeppelin-web/.eslintrc @@ -1,5 +1,5 @@ { - "preset": "google", + "extends": ["eslint:recommended", "google", "standard"], "env": { "browser": true, "jasmine": true, @@ -31,6 +31,20 @@ "process": false }, "rules": { + "array-bracket-spacing": 0, + "space-before-function-paren": 0, + "no-unneeded-ternary": 0, + "comma-dangle": 0, + "object-curly-spacing": 0, + "standard/object-curly-even-spacing": 0, + "arrow-parens": 0, + "require-jsdoc": 0, + "valid-jsdoc": 0, + "no-invalid-this": 0, + "no-console": 0, + "guard-for-in": 0, + "no-mixed-operators": 1, + "no-useless-escape": 1, "no-bitwise": 2, "camelcase": 2, "curly": 2, diff --git a/zeppelin-web/Gruntfile.js b/zeppelin-web/Gruntfile.js index b6c1859b85..764bbc2b99 100644 --- a/zeppelin-web/Gruntfile.js +++ b/zeppelin-web/Gruntfile.js @@ -104,13 +104,6 @@ module.exports = function(grunt) { files: ['bower.json'], tasks: ['wiredep'] }, - js: { - files: [ - '<%= yeoman.app %>/app/**/*.js', - '<%= yeoman.app %>/components/**/*.js' - ], - tasks: ['newer:eslint:all'], - }, html: { files: [ '<%= yeoman.app %>/**/*.html' @@ -120,7 +113,6 @@ module.exports = function(grunt) { jsTest: { files: ['test/spec/{,*/}*.js'], tasks: [ - 'newer:eslint:test', 'karma' ] }, @@ -148,24 +140,6 @@ module.exports = function(grunt) { } }, - eslint: { - all: { - src: [ - 'Gruntfile.js', - '<%= yeoman.app %>/app/**/*.js', - '<%= yeoman.app %>/components/**/*.js' - ] - }, - test: { - options: { - rules: { - 'no-undef': 0 - } - }, - src: ['test/spec/{,*/}*.js'] - } - }, - // Add vendor prefixed styles postcss: { options: { @@ -430,7 +404,6 @@ module.exports = function(grunt) { ]); grunt.registerTask('pre-webpack-dist', [ - 'eslint', 'htmlhint', 'wiredep', ]); diff --git a/zeppelin-web/package.json b/zeppelin-web/package.json index 99dc05801b..17bcb8dedd 100644 --- a/zeppelin-web/package.json +++ b/zeppelin-web/package.json @@ -8,14 +8,15 @@ "scripts": { "clean": "rimraf dist && rimraf .tmp", "postinstall": "bower install --silent", - "prebuild": "npm-run-all clean", + "prebuild": "npm-run-all clean lint:once", "build": "grunt pre-webpack-dist && webpack && grunt post-webpack-dist", + "lint:watch": "esw --watch src", + "lint:once": "eslint src", "predev": "grunt pre-webpack-dev", "dev:server": "webpack-dev-server --hot", "dev:helium": "HELIUM_BUNDLE_DEV=true webpack-dev-server --hot", "dev:watch": "grunt watch-webpack-dev", - "dev": "npm-run-all --parallel dev:server dev:watch", - "visdev": "npm-run-all --parallel visdev:server dev:watch", + "dev": "npm-run-all --parallel dev:server lint:watch dev:watch", "pretest": "npm install karma-phantomjs-launcher babel-polyfill", "test": "karma start karma.conf.js" }, @@ -33,7 +34,14 @@ "bower": "^1.8.0", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.26.1", + "eslint": "^3.19.0", "eslint-config-google": "^0.7.1", + "eslint-config-standard": "^10.2.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-node": "^4.2.2", + "eslint-plugin-promise": "^3.5.0", + "eslint-plugin-standard": "^3.0.1", + "eslint-watch": "^3.1.0", "express": "^4.14.0", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", @@ -47,7 +55,6 @@ "grunt-contrib-htmlmin": "^0.3.0", "grunt-contrib-uglify": "^0.4.0", "grunt-contrib-watch": "^0.6.1", - "grunt-eslint": "^19.0.0", "grunt-filerev": "^0.2.1", "grunt-htmlhint": "^0.9.13", "grunt-newer": "^0.7.0", @@ -70,11 +77,11 @@ "postcss-loader": "^1.2.1", "raw-loader": "^0.5.1", "rimraf": "^2.5.4", + "string-replace-webpack-plugin": "^0.1.3", "style-loader": "^0.13.1", "time-grunt": "^0.3.1", "webpack": "^1.14.0", - "webpack-dev-server": "^1.16.2", - "string-replace-webpack-plugin": "^0.1.3" + "webpack-dev-server": "^1.16.2" }, "repository": { "type": "git", diff --git a/zeppelin-web/src/app/app.controller.js b/zeppelin-web/src/app/app.controller.js index 30c96a1795..6c64a33d18 100644 --- a/zeppelin-web/src/app/app.controller.js +++ b/zeppelin-web/src/app/app.controller.js @@ -12,49 +12,48 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl); +angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl) -function MainCtrl($scope, $rootScope, $window, arrayOrderingSrv) { - 'ngInject'; +function MainCtrl ($scope, $rootScope, $window, arrayOrderingSrv) { + 'ngInject' - $scope.looknfeel = 'default'; + $scope.looknfeel = 'default' - var init = function() { - $scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false); - }; + let init = function () { + $scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false) + } - init(); + init() - $rootScope.$on('setIframe', function(event, data) { + $rootScope.$on('setIframe', function (event, data) { if (!event.defaultPrevented) { - $scope.asIframe = data; - event.preventDefault(); + $scope.asIframe = data + event.preventDefault() } - }); + }) - $rootScope.$on('setLookAndFeel', function(event, data) { + $rootScope.$on('setLookAndFeel', function (event, data) { if (!event.defaultPrevented && data && data !== '' && data !== $scope.looknfeel) { - $scope.looknfeel = data; - event.preventDefault(); + $scope.looknfeel = data + event.preventDefault() } - }); + }) // Set The lookAndFeel to default on every page - $rootScope.$on('$routeChangeStart', function(event, next, current) { - $rootScope.$broadcast('setLookAndFeel', 'default'); - }); + $rootScope.$on('$routeChangeStart', function (event, next, current) { + $rootScope.$broadcast('setLookAndFeel', 'default') + }) - $rootScope.noteName = function(note) { + $rootScope.noteName = function (note) { if (!_.isEmpty(note)) { - return arrayOrderingSrv.getNoteName(note); + return arrayOrderingSrv.getNoteName(note) } - }; + } - BootstrapDialog.defaultOptions.onshown = function() { - angular.element('#' + this.id).find('.btn:last').focus(); - }; + BootstrapDialog.defaultOptions.onshown = function () { + angular.element('#' + this.id).find('.btn:last').focus() + } // Remove BootstrapDialog animation - BootstrapDialog.configDefaultOptions({animate: false}); + BootstrapDialog.configDefaultOptions({animate: false}) } - diff --git a/zeppelin-web/src/app/app.controller.test.js b/zeppelin-web/src/app/app.controller.test.js index 329fb2186b..67d5034294 100644 --- a/zeppelin-web/src/app/app.controller.test.js +++ b/zeppelin-web/src/app/app.controller.test.js @@ -1,29 +1,28 @@ -describe('Controller: MainCtrl', function() { - beforeEach(angular.mock.module('zeppelinWebApp')); +describe('Controller: MainCtrl', function () { + beforeEach(angular.mock.module('zeppelinWebApp')) - var scope; - var rootScope; + let scope + let rootScope - beforeEach(inject(function($controller, $rootScope) { - rootScope = $rootScope; - scope = $rootScope.$new(); + beforeEach(inject(function ($controller, $rootScope) { + rootScope = $rootScope + scope = $rootScope.$new() $controller('MainCtrl', { $scope: scope - }); - })); + }) + })) - it('should attach "asIframe" to the scope and the default value should be false', function() { - expect(scope.asIframe).toBeDefined(); - expect(scope.asIframe).toEqual(false); - }); + it('should attach "asIframe" to the scope and the default value should be false', function () { + expect(scope.asIframe).toBeDefined() + expect(scope.asIframe).toEqual(false) + }) - it('should set the default value of "looknfeel to "default"', function() { - expect(scope.looknfeel).toEqual('default'); - }); + it('should set the default value of "looknfeel to "default"', function () { + expect(scope.looknfeel).toEqual('default') + }) - it('should set "asIframe" flag to true when a controller broadcasts setIframe event', function() { - rootScope.$broadcast('setIframe', true); - expect(scope.asIframe).toEqual(true); - }); - -}); + it('should set "asIframe" flag to true when a controller broadcasts setIframe event', function () { + rootScope.$broadcast('setIframe', true) + expect(scope.asIframe).toEqual(true) + }) +}) diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js index 40b19c1dbf..0775d38937 100644 --- a/zeppelin-web/src/app/app.js +++ b/zeppelin-web/src/app/app.js @@ -15,7 +15,7 @@ * limitations under the License. */ -var zeppelinWebApp = angular.module('zeppelinWebApp', [ +let zeppelinWebApp = angular.module('zeppelinWebApp', [ 'ngCookies', 'ngAnimate', 'ngRoute', @@ -35,22 +35,23 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [ 'ngResource', 'ngclipboard' ]) - .filter('breakFilter', function() { - return function(text) { + .filter('breakFilter', function () { + return function (text) { + // eslint-disable-next-line no-extra-boolean-cast if (!!text) { - return text.replace(/\n/g, '
'); + return text.replace(/\n/g, '
') } - }; + } }) - .config(function($httpProvider, $routeProvider, ngToastProvider) { + .config(function ($httpProvider, $routeProvider, ngToastProvider) { // withCredentials when running locally via grunt - $httpProvider.defaults.withCredentials = true; + $httpProvider.defaults.withCredentials = true - var visBundleLoad = { - load: ['heliumService', function(heliumService) { - return heliumService.load; + let visBundleLoad = { + load: ['heliumService', function (heliumService) { + return heliumService.load }] - }; + } $routeProvider .when('/', { @@ -107,67 +108,67 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [ }) .otherwise({ redirectTo: '/' - }); + }) ngToastProvider.configure({ dismissButton: true, dismissOnClick: false, combineDuplications: true, timeout: 6000 - }); + }) }) - //handel logout on API failure + // handel logout on API failure .config(function ($httpProvider, $provide) { $provide.factory('httpInterceptor', function ($q, $rootScope) { return { 'responseError': function (rejection) { if (rejection.status === 405) { - var data = {}; - data.info = ''; - $rootScope.$broadcast('session_logout', data); + let data = {} + data.info = '' + $rootScope.$broadcast('session_logout', data) } - $rootScope.$broadcast('httpResponseError', rejection); - return $q.reject(rejection); + $rootScope.$broadcast('httpResponseError', rejection) + return $q.reject(rejection) } - }; - }); - $httpProvider.interceptors.push('httpInterceptor'); + } + }) + $httpProvider.interceptors.push('httpInterceptor') }) - .constant('TRASH_FOLDER_ID', '~Trash'); + .constant('TRASH_FOLDER_ID', '~Trash') -function auth() { - var $http = angular.injector(['ng']).get('$http'); - var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv'); +function auth () { + let $http = angular.injector(['ng']).get('$http') + let baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv') // withCredentials when running locally via grunt - $http.defaults.withCredentials = true; + $http.defaults.withCredentials = true jQuery.ajaxSetup({ dataType: 'json', xhrFields: { withCredentials: true }, crossDomain: true - }); - return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function(response) { - zeppelinWebApp.run(function($rootScope) { - $rootScope.ticket = angular.fromJson(response.data).body; - }); - }, function(errorResponse) { + }) + return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function (response) { + zeppelinWebApp.run(function ($rootScope) { + $rootScope.ticket = angular.fromJson(response.data).body + }) + }, function (errorResponse) { // Handle error case - }); + }) } -function bootstrapApplication() { - zeppelinWebApp.run(function($rootScope, $location) { - $rootScope.$on('$routeChangeStart', function(event, next, current) { +function bootstrapApplication () { + zeppelinWebApp.run(function ($rootScope, $location) { + $rootScope.$on('$routeChangeStart', function (event, next, current) { if (!$rootScope.ticket && next.$$route && !next.$$route.publicAccess) { - $location.path('/'); + $location.path('/') } - }); - }); - angular.bootstrap(document, ['zeppelinWebApp']); + }) + }) + angular.bootstrap(document, ['zeppelinWebApp']) } -angular.element(document).ready(function() { - auth().then(bootstrapApplication); -}); +angular.element(document).ready(function () { + auth().then(bootstrapApplication) +}) diff --git a/zeppelin-web/src/app/configuration/configuration.controller.js b/zeppelin-web/src/app/configuration/configuration.controller.js index 70c9fe07bf..e4f4e5d657 100644 --- a/zeppelin-web/src/app/configuration/configuration.controller.js +++ b/zeppelin-web/src/app/configuration/configuration.controller.js @@ -12,38 +12,38 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl); +angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl) -function ConfigurationCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) { - 'ngInject'; +function ConfigurationCtrl ($scope, $rootScope, $http, baseUrlSrv, ngToast) { + 'ngInject' - $scope.configrations = []; - $scope._ = _; - ngToast.dismiss(); + $scope.configrations = [] + $scope._ = _ + ngToast.dismiss() - var getConfigurations = function() { - $http.get(baseUrlSrv.getRestApiBase() + '/configurations/all'). - success(function(data, status, headers, config) { - $scope.configurations = data.body; - }). - error(function(data, status, headers, config) { + let getConfigurations = function () { + $http.get(baseUrlSrv.getRestApiBase() + '/configurations/all') + .success(function (data, status, headers, config) { + $scope.configurations = data.body + }) + .error(function (data, status, headers, config) { if (status === 401) { ngToast.danger({ content: 'You don\'t have permission on this page', verticalPosition: 'bottom', timeout: '3000' - }); - setTimeout(function() { - window.location.replace('/'); - }, 3000); + }) + setTimeout(function () { + window.location.replace('/') + }, 3000) } - console.log('Error %o %o', status, data.message); - }); - }; + console.log('Error %o %o', status, data.message) + }) + } - var init = function() { - getConfigurations(); - }; + let init = function () { + getConfigurations() + } - init(); + init() } diff --git a/zeppelin-web/src/app/credential/credential.controller.js b/zeppelin-web/src/app/credential/credential.controller.js index 30edfe9458..eb649c8aa2 100644 --- a/zeppelin-web/src/app/credential/credential.controller.js +++ b/zeppelin-web/src/app/credential/credential.controller.js @@ -12,179 +12,178 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl); +angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl) -function CredentialCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) { - 'ngInject'; +function CredentialCtrl ($scope, $rootScope, $http, baseUrlSrv, ngToast) { + 'ngInject' - $scope._ = _; - ngToast.dismiss(); + $scope._ = _ + ngToast.dismiss() - $scope.credentialInfo = []; - $scope.showAddNewCredentialInfo = false; - $scope.availableInterpreters = []; + $scope.credentialInfo = [] + $scope.showAddNewCredentialInfo = false + $scope.availableInterpreters = [] - var getCredentialInfo = function() { - $http.get(baseUrlSrv.getRestApiBase() + '/credential'). - success(function(data, status, headers, config) { - $scope.credentialInfo = _.map(data.body.userCredentials, function(value, prop) { - return {entity: prop, password: value.password, username: value.username}; - }); - console.log('Success %o %o', status, $scope.credentialInfo); - }). - error(function(data, status, headers, config) { + let getCredentialInfo = function () { + $http.get(baseUrlSrv.getRestApiBase() + '/credential') + .success(function (data, status, headers, config) { + $scope.credentialInfo = _.map(data.body.userCredentials, function (value, prop) { + return {entity: prop, password: value.password, username: value.username} + }) + console.log('Success %o %o', status, $scope.credentialInfo) + }) + .error(function (data, status, headers, config) { if (status === 401) { ngToast.danger({ content: 'You don\'t have permission on this page', verticalPosition: 'bottom', timeout: '3000' - }); - setTimeout(function() { - window.location.replace('/'); - }, 3000); + }) + setTimeout(function () { + window.location.replace('/') + }, 3000) } - console.log('Error %o %o', status, data.message); - }); - }; + console.log('Error %o %o', status, data.message) + }) + } - $scope.addNewCredentialInfo = function() { + $scope.addNewCredentialInfo = function () { if ($scope.entity && _.isEmpty($scope.entity.trim()) && $scope.username && _.isEmpty($scope.username.trim())) { ngToast.danger({ content: 'Username \\ Entity can not be empty.', verticalPosition: 'bottom', timeout: '3000' - }); - return; + }) + return } - var newCredential = { + let newCredential = { 'entity': $scope.entity, 'username': $scope.username, 'password': $scope.password - }; + } - $http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential). - success(function(data, status, headers, config) { + $http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential) + .success(function (data, status, headers, config) { ngToast.success({ content: 'Successfully saved credentials.', verticalPosition: 'bottom', timeout: '3000' - }); - $scope.credentialInfo.push(newCredential); - resetCredentialInfo(); - $scope.showAddNewCredentialInfo = false; - console.log('Success %o %o', status, data.message); - }). - error(function(data, status, headers, config) { + }) + $scope.credentialInfo.push(newCredential) + resetCredentialInfo() + $scope.showAddNewCredentialInfo = false + console.log('Success %o %o', status, data.message) + }) + .error(function (data, status, headers, config) { ngToast.danger({ content: 'Error saving credentials', verticalPosition: 'bottom', timeout: '3000' - }); - console.log('Error %o %o', status, data.message); - }); - }; + }) + console.log('Error %o %o', status, data.message) + }) + } - var getAvailableInterpreters = function() { + let getAvailableInterpreters = function () { $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting') - .success(function(data, status, headers, config) { - for (var setting = 0; setting < data.body.length; setting++) { + .success(function (data, status, headers, config) { + for (let setting = 0; setting < data.body.length; setting++) { $scope.availableInterpreters.push( - data.body[setting].group + '.' + data.body[setting].name); + data.body[setting].group + '.' + data.body[setting].name) } angular.element('#entityname').autocomplete({ source: $scope.availableInterpreters, - select: function(event, selected) { - $scope.entity = selected.item.value; - return false; + select: function (event, selected) { + $scope.entity = selected.item.value + return false } - }); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); - }; + }) + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) + } - $scope.toggleAddNewCredentialInfo = function() { + $scope.toggleAddNewCredentialInfo = function () { if ($scope.showAddNewCredentialInfo) { - $scope.showAddNewCredentialInfo = false; + $scope.showAddNewCredentialInfo = false } else { - $scope.showAddNewCredentialInfo = true; + $scope.showAddNewCredentialInfo = true } - }; + } - $scope.cancelCredentialInfo = function() { - $scope.showAddNewCredentialInfo = false; - resetCredentialInfo(); - }; + $scope.cancelCredentialInfo = function () { + $scope.showAddNewCredentialInfo = false + resetCredentialInfo() + } - var resetCredentialInfo = function() { - $scope.entity = ''; - $scope.username = ''; - $scope.password = ''; - }; + const resetCredentialInfo = function () { + $scope.entity = '' + $scope.username = '' + $scope.password = '' + } - $scope.copyOriginCredentialsInfo = function() { + $scope.copyOriginCredentialsInfo = function () { ngToast.info({ content: 'Since entity is a unique key, you can edit only username & password', verticalPosition: 'bottom', timeout: '3000' - }); - }; + }) + } - $scope.updateCredentialInfo = function(form, data, entity) { - var request = { + $scope.updateCredentialInfo = function (form, data, entity) { + let request = { entity: entity, username: data.username, password: data.password - }; + } - $http.put(baseUrlSrv.getRestApiBase() + '/credential/', request). - success(function(data, status, headers, config) { - var index = _.findIndex($scope.credentialInfo, {'entity': entity}); - $scope.credentialInfo[index] = request; - return true; - }). - error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); + $http.put(baseUrlSrv.getRestApiBase() + '/credential/', request) + .success(function (data, status, headers, config) { + let index = _.findIndex($scope.credentialInfo, {'entity': entity}) + $scope.credentialInfo[index] = request + return true + }) + .error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) ngToast.danger({ content: 'We couldn\'t save the credential', verticalPosition: 'bottom', timeout: '3000' - }); - form.$show(); - }); - return false; - }; + }) + form.$show() + }) + return false + } - $scope.removeCredentialInfo = function(entity) { + $scope.removeCredentialInfo = function (entity) { BootstrapDialog.confirm({ closable: false, closeByBackdrop: false, closeByKeyboard: false, title: '', message: 'Do you want to delete this credential information?', - callback: function(result) { + callback: function (result) { if (result) { - $http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity). - success(function(data, status, headers, config) { - var index = _.findIndex($scope.credentialInfo, {'entity': entity}); - $scope.credentialInfo.splice(index, 1); - console.log('Success %o %o', status, data.message); - }). - error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); + $http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity) + .success(function (data, status, headers, config) { + let index = _.findIndex($scope.credentialInfo, {'entity': entity}) + $scope.credentialInfo.splice(index, 1) + console.log('Success %o %o', status, data.message) + }) + .error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) } } - }); - }; + }) + } - var init = function() { - getAvailableInterpreters(); - getCredentialInfo(); - }; + let init = function () { + getAvailableInterpreters() + getCredentialInfo() + } - init(); + init() } - diff --git a/zeppelin-web/src/app/handsontable/handsonHelper.js b/zeppelin-web/src/app/handsontable/handsonHelper.js index ccfa87a43c..8d724c0e4b 100644 --- a/zeppelin-web/src/app/handsontable/handsonHelper.js +++ b/zeppelin-web/src/app/handsontable/handsonHelper.js @@ -16,15 +16,15 @@ * HandsonHelper class */ export default class HandsonHelper { - constructor(columns, rows, comment) { - this.columns = columns || []; - this.rows = rows || []; - this.comment = comment || ''; - this._numericValidator = this._numericValidator.bind(this); - }; + constructor (columns, rows, comment) { + this.columns = columns || [] + this.rows = rows || [] + this.comment = comment || '' + this._numericValidator = this._numericValidator.bind(this) + } - getHandsonTableConfig(columns, columnNames, resultRows) { - var self = this; + getHandsonTableConfig (columns, columnNames, resultRows) { + let self = this return { colHeaders: columnNames, data: resultRows, @@ -41,159 +41,161 @@ export default class HandsonHelper { fillHandle: false, fragmentSelection: true, disableVisualSelection: true, - cells: function(ro, co, pro) { - var cellProperties = {}; - var colType = columns[co].type; - cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) { - self._cellRenderer(instance, td, row, col, prop, value, cellProperties, colType); - }; - return cellProperties; - }, - afterGetColHeader: function(col, TH) { - var instance = this; - var menu = self._buildDropDownMenu(columns[col].type); - var button = self._buildTypeSwitchButton(); - - self._addButtonMenuEvent(button, menu); - - Handsontable.Dom.addEvent(menu, 'click', function(event) { - if (event.target.nodeName === 'LI') { - self._setColumnType(columns, event.target.data.colType, instance, col); - } - }); - if (TH.firstChild.lastChild.nodeName === 'BUTTON') { - TH.firstChild.removeChild(TH.firstChild.lastChild); + cells: function (ro, co, pro) { + let cellProperties = {} + let colType = columns[co].type + cellProperties.renderer = function (instance, td, row, col, prop, value, cellProperties) { + self._cellRenderer(instance, td, row, col, prop, value, cellProperties, colType) } - TH.firstChild.appendChild(button); - TH.style['white-space'] = 'normal'; + return cellProperties + }, + afterGetColHeader: function (col, TH) { + let instance = this + let menu = self._buildDropDownMenu(columns[col].type) + let button = self._buildTypeSwitchButton() + + self._addButtonMenuEvent(button, menu) + + Handsontable.Dom.addEvent(menu, 'click', function (event) { + if (event.target.nodeName === 'LI') { + self._setColumnType(columns, event.target.data.colType, instance, col) + } + }) + if (TH.firstChild.lastChild.nodeName === 'BUTTON') { + TH.firstChild.removeChild(TH.firstChild.lastChild) + } + TH.firstChild.appendChild(button) + TH.style['white-space'] = 'normal' } - }; - }; + } + } /* ** Private Service Functions */ - _addButtonMenuEvent(button, menu) { - Handsontable.Dom.addEvent(button, 'click', function(event) { - var changeTypeMenu; - var position; - var removeMenu; + _addButtonMenuEvent (button, menu) { + Handsontable.Dom.addEvent(button, 'click', function (event) { + let changeTypeMenu + let position + let removeMenu - document.body.appendChild(menu); + document.body.appendChild(menu) - event.preventDefault(); - event.stopImmediatePropagation(); + event.preventDefault() + event.stopImmediatePropagation() - changeTypeMenu = document.querySelectorAll('.changeTypeMenu'); + changeTypeMenu = document.querySelectorAll('.changeTypeMenu') - for (var i = 0, len = changeTypeMenu.length; i < len; i++) { - changeTypeMenu[i].style.display = 'none'; + for (let i = 0, len = changeTypeMenu.length; i < len; i++) { + changeTypeMenu[i].style.display = 'none' } - menu.style.display = 'block'; - position = button.getBoundingClientRect(); + menu.style.display = 'block' + position = button.getBoundingClientRect() - menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px'; - menu.style.left = (position.left) + 'px'; + menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px' + menu.style.left = (position.left) + 'px' - removeMenu = function(event) { + removeMenu = function (event) { if (menu.parentNode) { - menu.parentNode.removeChild(menu); + menu.parentNode.removeChild(menu) } - }; - Handsontable.Dom.removeEvent(document, 'click', removeMenu); - Handsontable.Dom.addEvent(document, 'click', removeMenu); - }); + } + Handsontable.Dom.removeEvent(document, 'click', removeMenu) + Handsontable.Dom.addEvent(document, 'click', removeMenu) + }) } - _buildDropDownMenu(activeCellType) { - var menu = document.createElement('UL'); - var types = ['text', 'numeric', 'date']; - var item; + _buildDropDownMenu (activeCellType) { + let menu = document.createElement('UL') + let types = ['text', 'numeric', 'date'] + let item - menu.className = 'changeTypeMenu'; + menu.className = 'changeTypeMenu' - for (var i = 0, len = types.length; i < len; i++) { - item = document.createElement('LI'); + for (let i = 0, len = types.length; i < len; i++) { + item = document.createElement('LI') if ('innerText' in item) { - item.innerText = types[i]; + item.innerText = types[i] } else { - item.textContent = types[i]; + item.textContent = types[i] } - item.data = {'colType': types[i]}; + item.data = {'colType': types[i]} if (activeCellType === types[i]) { - item.className = 'active'; + item.className = 'active' } - menu.appendChild(item); + menu.appendChild(item) } - return menu; + return menu } - _buildTypeSwitchButton() { - var button = document.createElement('BUTTON'); + _buildTypeSwitchButton () { + let button = document.createElement('BUTTON') - button.innerHTML = '\u25BC'; - button.className = 'changeType'; + button.innerHTML = '\u25BC' + button.className = 'changeType' - return button; + return button } - _isNumeric(value) { + _isNumeric (value) { if (!isNaN(value)) { if (value.length !== 0) { if (Number(value) <= Number.MAX_SAFE_INTEGER && Number(value) >= Number.MIN_SAFE_INTEGER) { - return true; + return true } } } - return false; + return false } - _cellRenderer(instance, td, row, col, prop, value, cellProperties, colType) { + _cellRenderer (instance, td, row, col, prop, value, cellProperties, colType) { if (colType === 'numeric' && this._isNumeric(value)) { - cellProperties.format = '0,0.[00000]'; - td.style.textAlign = 'left'; - Handsontable.renderers.NumericRenderer.apply(this, arguments); - } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) { - td.innerHTML = value.substring('%html'.length); + cellProperties.format = '0,0.[00000]' + td.style.textAlign = 'left' + // eslint-disable-next-line prefer-rest-params + Handsontable.renderers.NumericRenderer.apply(this, arguments) + } else if (value.length > '%html'.length && value.substring(0, '%html '.length) === '%html ') { + td.innerHTML = value.substring('%html'.length) } else { - Handsontable.renderers.TextRenderer.apply(this, arguments); + // eslint-disable-next-line prefer-rest-params + Handsontable.renderers.TextRenderer.apply(this, arguments) } } - _dateValidator(value, callback) { - var d = moment(value); - return callback(d.isValid()); + _dateValidator (value, callback) { + let d = moment(value) + return callback(d.isValid()) } - _numericValidator(value, callback) { - return callback(this._isNumeric(value)); + _numericValidator (value, callback) { + return callback(this._isNumeric(value)) } - _setColumnType(columns, type, instance, col) { - columns[col].type = type; - this._setColumnValidator(columns, col); - instance.updateSettings({columns: columns}); - instance.validateCells(null); + _setColumnType (columns, type, instance, col) { + columns[col].type = type + this._setColumnValidator(columns, col) + instance.updateSettings({columns: columns}) + instance.validateCells(null) if (this._isColumnSorted(instance, col)) { - instance.sort(col, instance.sortOrder); + instance.sort(col, instance.sortOrder) } } - _isColumnSorted(instance, col) { - return instance.sortingEnabled && instance.sortColumn === col; + _isColumnSorted (instance, col) { + return instance.sortingEnabled && instance.sortColumn === col } - _setColumnValidator(columns, col) { + _setColumnValidator (columns, col) { if (columns[col].type === 'numeric') { - columns[col].validator = this._numericValidator; + columns[col].validator = this._numericValidator } else if (columns[col].type === 'date') { - columns[col].validator = this._dateValidator; + columns[col].validator = this._dateValidator } else { - columns[col].validator = null; + columns[col].validator = null } } } diff --git a/zeppelin-web/src/app/helium/helium.config.js b/zeppelin-web/src/app/helium/helium.config.js index e21fe198c8..ace7136890 100644 --- a/zeppelin-web/src/app/helium/helium.config.js +++ b/zeppelin-web/src/app/helium/helium.config.js @@ -16,85 +16,86 @@ export const HeliumConfFieldType = { NUMBER: 'number', JSON: 'json', STRING: 'string', -}; +} /** * @param persisted including `type`, `description`, `defaultValue` for each conf key * @param spec including `value` for each conf key */ -export function mergePersistedConfWithSpec(persisted, spec) { - const confs = []; +export function mergePersistedConfWithSpec (persisted, spec) { + const confs = [] - for(let name in spec) { - const specField = spec[name]; - const persistedValue = persisted[name]; + for (let name in spec) { + const specField = spec[name] + const persistedValue = persisted[name] - const value = (persistedValue) ? persistedValue : specField.defaultValue; + const value = (persistedValue) ? persistedValue : specField.defaultValue const merged = { - name: name, type: specField.type, description: specField.description, - value: value, defaultValue: specField.defaultValue, - }; - - confs.push(merged); - } - - return confs; -} - -export function createPackageConf(defaultPackages, persistedPackacgeConfs) { - let packageConfs = {}; - - for (let name in defaultPackages) { - const pkgInfo = defaultPackages[name]; - - const configSpec = pkgInfo.pkg.config; - if (!configSpec) { continue; } - - const version = pkgInfo.pkg.version; - if (!version) { continue; } - - let config = {}; - if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) { - config = persistedPackacgeConfs[name][version]; + name: name, + type: specField.type, + description: specField.description, + value: value, + defaultValue: specField.defaultValue, } - const confs = mergePersistedConfWithSpec(config, configSpec); - packageConfs[name] = confs; + confs.push(merged) } - return packageConfs; + return confs } -export function parseConfigValue(type, stringified) { - let value = stringified; +export function createPackageConf (defaultPackages, persistedPackacgeConfs) { + let packageConfs = {} + + for (let name in defaultPackages) { + const pkgInfo = defaultPackages[name] + + const configSpec = pkgInfo.pkg.config + if (!configSpec) { continue } + + const version = pkgInfo.pkg.version + if (!version) { continue } + + let config = {} + if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) { + config = persistedPackacgeConfs[name][version] + } + + const confs = mergePersistedConfWithSpec(config, configSpec) + packageConfs[name] = confs + } + + return packageConfs +} + +export function parseConfigValue (type, stringified) { + let value = stringified try { if (HeliumConfFieldType.NUMBER === type) { - value = parseFloat(stringified); + value = parseFloat(stringified) } else if (HeliumConfFieldType.JSON === type) { - value = JSON.parse(stringified); + value = JSON.parse(stringified) } - } catch(error) { + } catch (error) { // return just the stringified one - console.error(`Failed to parse conf type ${type}, value ${value}`); + console.error(`Failed to parse conf type ${type}, value ${value}`) } - return value; + return value } /** * create persistable config object */ -export function createPersistableConfig(currentConf) { +export function createPersistableConfig (currentConf) { // persist key-value only // since other info (e.g type, desc) can be provided by default config const filtered = currentConf.reduce((acc, c) => { - let value = parseConfigValue(c.type, c.value); - acc[c.name] = value; - return acc; - }, {}); + let value = parseConfigValue(c.type, c.value) + acc[c.name] = value + return acc + }, {}) - return filtered; + return filtered } - - diff --git a/zeppelin-web/src/app/helium/helium.controller.js b/zeppelin-web/src/app/helium/helium.controller.js index 615f81161f..7a112e2065 100644 --- a/zeppelin-web/src/app/helium/helium.controller.js +++ b/zeppelin-web/src/app/helium/helium.controller.js @@ -12,17 +12,17 @@ * limitations under the License. */ -import { HeliumType, } from '../../components/helium/helium-type'; +import { HeliumType, } from '../../components/helium/helium-type' -export default function HeliumCtrl($scope, $rootScope, $sce, +export default function HeliumCtrl ($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService) { - 'ngInject'; + 'ngInject' - $scope.pkgSearchResults = {}; - $scope.defaultPackages = {}; - $scope.showVersions = {}; - $scope.bundleOrder = []; - $scope.bundleOrderChanged = false; + $scope.pkgSearchResults = {} + $scope.defaultPackages = {} + $scope.showVersions = {} + $scope.bundleOrder = [] + $scope.bundleOrderChanged = false $scope.vizTypePkg = {} $scope.spellTypePkg = {} $scope.intpTypePkg = {} @@ -30,168 +30,168 @@ export default function HeliumCtrl($scope, $rootScope, $sce, $scope.numberOfEachPackageByType = {} $scope.allPackageTypes = [HeliumType][0] $scope.pkgListByType = 'VISUALIZATION' - $scope.defaultPackageConfigs = {}; // { pkgName, [{name, type, desc, value, defaultValue}] } - $scope.intpDefaultIcon = $sce.trustAsHtml(''); + $scope.defaultPackageConfigs = {} // { pkgName, [{name, type, desc, value, defaultValue}] } + $scope.intpDefaultIcon = $sce.trustAsHtml('') - function init() { + function init () { // get all package info and set config heliumService.getAllPackageInfoAndDefaultPackages() .then(({ pkgSearchResults, defaultPackages }) => { // pagination - $scope.itemsPerPage = 10; - $scope.currentPage = 1; - $scope.maxSize = 5; - - $scope.pkgSearchResults = pkgSearchResults; - $scope.defaultPackages = defaultPackages; - classifyPkgType($scope.defaultPackages); - + $scope.itemsPerPage = 10 + $scope.currentPage = 1 + $scope.maxSize = 5 + + $scope.pkgSearchResults = pkgSearchResults + $scope.defaultPackages = defaultPackages + classifyPkgType($scope.defaultPackages) + return heliumService.getAllPackageConfigs() }) .then(defaultPackageConfigs => { - $scope.defaultPackageConfigs = defaultPackageConfigs; - }); + $scope.defaultPackageConfigs = defaultPackageConfigs + }) // 2. get vis package order heliumService.getVisualizationPackageOrder() .then(visPackageOrder => { - $scope.bundleOrder = visPackageOrder; - $scope.bundleOrderChanged = false; - }); - }; + $scope.bundleOrder = visPackageOrder + $scope.bundleOrderChanged = false + }) + } - var orderPackageByPubDate = function(a, b) { + let orderPackageByPubDate = function (a, b) { if (!a.pkg.published) { // Because local registry pkgs don't have 'published' field, put current time instead to show them first a.pkg.published = new Date().getTime() } - return new Date(a.pkg.published).getTime() - new Date(b.pkg.published).getTime(); - }; + return new Date(a.pkg.published).getTime() - new Date(b.pkg.published).getTime() + } - var classifyPkgType = function(packageInfo) { - var allTypesOfPkg = {}; - var vizTypePkg = []; - var spellTypePkg = []; - var intpTypePkg = []; - var appTypePkg = []; + const classifyPkgType = function (packageInfo) { + let allTypesOfPkg = {} + let vizTypePkg = [] + let spellTypePkg = [] + let intpTypePkg = [] + let appTypePkg = [] - var packageInfoArr = Object.keys(packageInfo).map(key => packageInfo[key]) - packageInfoArr = packageInfoArr.sort(orderPackageByPubDate).reverse(); + let packageInfoArr = Object.keys(packageInfo).map(key => packageInfo[key]) + packageInfoArr = packageInfoArr.sort(orderPackageByPubDate).reverse() - for (var name in packageInfoArr) { - var pkgs = packageInfoArr[name]; - var pkgType = pkgs.pkg.type; + for (let name in packageInfoArr) { + let pkgs = packageInfoArr[name] + let pkgType = pkgs.pkg.type switch (pkgType) { case HeliumType.VISUALIZATION: - vizTypePkg.push(pkgs); - break; + vizTypePkg.push(pkgs) + break case HeliumType.SPELL: - spellTypePkg.push(pkgs); - break; + spellTypePkg.push(pkgs) + break case HeliumType.INTERPRETER: - intpTypePkg.push(pkgs); - break; + intpTypePkg.push(pkgs) + break case HeliumType.APPLICATION: - appTypePkg.push(pkgs); - break; + appTypePkg.push(pkgs) + break } } - var pkgsArr = [ + let pkgsArr = [ vizTypePkg, spellTypePkg, intpTypePkg, appTypePkg ] - for (var idx in _.keys(HeliumType)) { - allTypesOfPkg[_.keys(HeliumType)[idx]] = pkgsArr[idx]; + for (let idx in _.keys(HeliumType)) { + allTypesOfPkg[_.keys(HeliumType)[idx]] = pkgsArr[idx] } - - $scope.allTypesOfPkg = allTypesOfPkg; - }; + + $scope.allTypesOfPkg = allTypesOfPkg + } $scope.bundleOrderListeners = { - accept: function(sourceItemHandleScope, destSortableScope) {return true;}, - itemMoved: function(event) {}, - orderChanged: function(event) { - $scope.bundleOrderChanged = true; + accept: function (sourceItemHandleScope, destSortableScope) { return true }, + itemMoved: function (event) {}, + orderChanged: function (event) { + $scope.bundleOrderChanged = true } - }; + } - $scope.saveBundleOrder = function() { - var confirm = BootstrapDialog.confirm({ + $scope.saveBundleOrder = function () { + const confirm = BootstrapDialog.confirm({ closable: false, closeByBackdrop: false, closeByKeyboard: false, title: '', message: 'Save changes?', - callback: function(result) { + callback: function (result) { if (result) { - confirm.$modalFooter.find('button').addClass('disabled'); + confirm.$modalFooter.find('button').addClass('disabled') confirm.$modalFooter.find('button:contains("OK")') - .html(' Enabling'); - heliumService.setVisualizationPackageOrder($scope.bundleOrder). - success(function(data, status) { - init(); - confirm.close(); - }). - error(function(data, status) { - confirm.close(); - console.log('Failed to save order'); + .html(' Enabling') + heliumService.setVisualizationPackageOrder($scope.bundleOrder) + .success(function (data, status) { + init() + confirm.close() + }) + .error(function (data, status) { + confirm.close() + console.log('Failed to save order') BootstrapDialog.show({ title: 'Error on saving order ', message: data.message - }); - }); - return false; + }) + }) + return false } } - }); - }; + }) + } - var getLicense = function(name, artifact) { - var filteredPkgSearchResults = _.filter($scope.defaultPackages[name], function(p) { - return p.artifact === artifact; - }); + let getLicense = function (name, artifact) { + let filteredPkgSearchResults = _.filter($scope.defaultPackages[name], function (p) { + return p.artifact === artifact + }) - var license; + let license if (filteredPkgSearchResults.length === 0) { - filteredPkgSearchResults = _.filter($scope.pkgSearchResults[name], function(p) { - return p.pkg.artifact === artifact; - }); + filteredPkgSearchResults = _.filter($scope.pkgSearchResults[name], function (p) { + return p.pkg.artifact === artifact + }) if (filteredPkgSearchResults.length > 0) { - license = filteredPkgSearchResults[0].pkg.license; + license = filteredPkgSearchResults[0].pkg.license } } else { - license = filteredPkgSearchResults[0].license; + license = filteredPkgSearchResults[0].license } if (!license) { - license = 'Unknown'; + license = 'Unknown' } - return license; + return license } - const getHeliumTypeText = function(type) { + const getHeliumTypeText = function (type) { if (type === HeliumType.VISUALIZATION) { - return `${type}`; // eslint-disable-line max-len + return `${type}` // eslint-disable-line max-len } else if (type === HeliumType.SPELL) { - return `${type}`; // eslint-disable-line max-len + return `${type}` // eslint-disable-line max-len } else { - return type; + return type } } - $scope.enable = function(name, artifact, type, groupId, description) { - var license = getLicense(name, artifact); - var mavenArtifactInfoToHTML = groupId +':'+ artifact.split('@')[0] + ':' + artifact.split('@')[1]; - var zeppelinVersion = $rootScope.zeppelinVersion; - var url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/manual/interpreterinstallation.html'; + $scope.enable = function (name, artifact, type, groupId, description) { + let license = getLicense(name, artifact) + let mavenArtifactInfoToHTML = groupId + ':' + artifact.split('@')[0] + ':' + artifact.split('@')[1] + let zeppelinVersion = $rootScope.zeppelinVersion + let url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/manual/interpreterinstallation.html' - var confirm = '' + let confirm = '' if (type === HeliumType.INTERPRETER) { confirm = BootstrapDialog.show({ title: '', @@ -201,11 +201,11 @@ export default function HeliumCtrl($scope, $rootScope, $sce, ' and all of its transitive dependencies into interpreter/interpreter-name directory.

' + '

' +
         './bin/install-interpreter.sh --name "interpreter-name" --artifact ' +
-        mavenArtifactInfoToHTML +' 
' + + mavenArtifactInfoToHTML + ' ' + '

After restart Zeppelin, create interpreter setting and bind it with your note. ' + 'For more detailed information, see Interpreter Installation.

' - }); + }) } else { confirm = BootstrapDialog.confirm({ closable: false, @@ -226,136 +226,136 @@ export default function HeliumCtrl($scope, $rootScope, $sce, `
${license}
`, callback: function (result) { if (result) { - confirm.$modalFooter.find('button').addClass('disabled'); + confirm.$modalFooter.find('button').addClass('disabled') confirm.$modalFooter.find('button:contains("OK")') - .html(' Enabling'); + .html(' Enabling') heliumService.enable(name, artifact, type).success(function (data, status) { - init(); - confirm.close(); + init() + confirm.close() }).error(function (data, status) { - confirm.close(); - console.log('Failed to enable package %o %o. %o', name, artifact, data); + confirm.close() + console.log('Failed to enable package %o %o. %o', name, artifact, data) BootstrapDialog.show({ title: 'Error on enabling ' + name, message: data.message - }); - }); - return false; + }) + }) + return false } } - }); + }) } - }; + } - $scope.disable = function(name, artifact) { - var confirm = BootstrapDialog.confirm({ + $scope.disable = function (name, artifact) { + const confirm = BootstrapDialog.confirm({ closable: false, closeByBackdrop: false, closeByKeyboard: false, title: '
Do you want to disable Helium Package?
', message: artifact, - callback: function(result) { + callback: function (result) { if (result) { - confirm.$modalFooter.find('button').addClass('disabled'); + confirm.$modalFooter.find('button').addClass('disabled') confirm.$modalFooter.find('button:contains("OK")') - .html(' Disabling'); - heliumService.disable(name). - success(function(data, status) { - init(); - confirm.close(); - }). - error(function(data, status) { - confirm.close(); - console.log('Failed to disable package %o. %o', name, data); + .html(' Disabling') + heliumService.disable(name) + .success(function (data, status) { + init() + confirm.close() + }) + .error(function (data, status) { + confirm.close() + console.log('Failed to disable package %o. %o', name, data) BootstrapDialog.show({ title: 'Error on disabling ' + name, message: data.message - }); - }); - return false; + }) + }) + return false } } - }); - }; + }) + } - $scope.toggleVersions = function(pkgName) { + $scope.toggleVersions = function (pkgName) { if ($scope.showVersions[pkgName]) { - $scope.showVersions[pkgName] = false; + $scope.showVersions[pkgName] = false } else { - $scope.showVersions[pkgName] = true; + $scope.showVersions[pkgName] = true } - }; + } - $scope.isLocalPackage = function(pkgSearchResult) { - const pkg = pkgSearchResult.pkg; - return pkg.artifact && !pkg.artifact.includes('@'); - }; + $scope.isLocalPackage = function (pkgSearchResult) { + const pkg = pkgSearchResult.pkg + return pkg.artifact && !pkg.artifact.includes('@') + } - $scope.hasNpmLink = function(pkgSearchResult) { - const pkg = pkgSearchResult.pkg; + $scope.hasNpmLink = function (pkgSearchResult) { + const pkg = pkgSearchResult.pkg return (pkg.type === HeliumType.SPELL || pkg.type === HeliumType.VISUALIZATION) && - !$scope.isLocalPackage(pkgSearchResult); - }; + !$scope.isLocalPackage(pkgSearchResult) + } - $scope.hasMavenLink = function(pkgSearchResult) { - const pkg = pkgSearchResult.pkg; + $scope.hasMavenLink = function (pkgSearchResult) { + const pkg = pkgSearchResult.pkg return (pkg.type === HeliumType.APPLICATION || pkg.type === HeliumType.INTERPRETER) && - !$scope.isLocalPackage(pkgSearchResult); - }; + !$scope.isLocalPackage(pkgSearchResult) + } - $scope.getPackageSize = function(pkgSearchResult, targetPkgType) { - var result = [] + $scope.getPackageSize = function (pkgSearchResult, targetPkgType) { + let result = [] _.map(pkgSearchResult, function (pkg) { result.push(_.find(pkg, {type: targetPkgType})) }) return _.compact(result).length } - $scope.configExists = function(pkgSearchResult) { + $scope.configExists = function (pkgSearchResult) { // helium package config is persisted per version - return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact; - }; - - $scope.configOpened = function(pkgSearchResult) { - return pkgSearchResult.configOpened && !pkgSearchResult.configFetching; - }; - - $scope.getConfigButtonClass = function(pkgSearchResult) { - return (pkgSearchResult.configOpened && pkgSearchResult.configFetching) ? - 'disabled' : ''; + return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact } - $scope.toggleConfigButton = function(pkgSearchResult) { + $scope.configOpened = function (pkgSearchResult) { + return pkgSearchResult.configOpened && !pkgSearchResult.configFetching + } + + $scope.getConfigButtonClass = function (pkgSearchResult) { + return (pkgSearchResult.configOpened && pkgSearchResult.configFetching) + ? 'disabled' : '' + } + + $scope.toggleConfigButton = function (pkgSearchResult) { if (pkgSearchResult.configOpened) { - pkgSearchResult.configOpened = false; - return; + pkgSearchResult.configOpened = false + return } - const pkg = pkgSearchResult.pkg; - const pkgName = pkg.name; - pkgSearchResult.configFetching = true; - pkgSearchResult.configOpened = true; + const pkg = pkgSearchResult.pkg + const pkgName = pkg.name + pkgSearchResult.configFetching = true + pkgSearchResult.configOpened = true heliumService.getSinglePackageConfigs(pkg) .then(confs => { - $scope.defaultPackageConfigs[pkgName] = confs; - pkgSearchResult.configFetching = false; - }); - }; + $scope.defaultPackageConfigs[pkgName] = confs + pkgSearchResult.configFetching = false + }) + } - $scope.saveConfig = function(pkgSearchResult) { - const pkgName = pkgSearchResult.pkg.name; - const currentConf = $scope.defaultPackageConfigs[pkgName]; + $scope.saveConfig = function (pkgSearchResult) { + const pkgName = pkgSearchResult.pkg.name + const currentConf = $scope.defaultPackageConfigs[pkgName] heliumService.saveConfig(pkgSearchResult.pkg, currentConf, () => { // close after config is saved - pkgSearchResult.configOpened = false; - }); - }; + pkgSearchResult.configOpened = false + }) + } - $scope.getDescriptionText = function(pkgSearchResult) { - return $sce.trustAsHtml(pkgSearchResult.pkg.description); - }; + $scope.getDescriptionText = function (pkgSearchResult) { + return $sce.trustAsHtml(pkgSearchResult.pkg.description) + } - init(); + init() } diff --git a/zeppelin-web/src/app/helium/index.js b/zeppelin-web/src/app/helium/index.js index 632969e236..2b27d6036c 100644 --- a/zeppelin-web/src/app/helium/index.js +++ b/zeppelin-web/src/app/helium/index.js @@ -12,8 +12,7 @@ * limitations under the License. */ -import HeliumController from './helium.controller'; +import HeliumController from './helium.controller' angular.module('zeppelinWebApp') - .controller('HeliumCtrl', HeliumController); - + .controller('HeliumCtrl', HeliumController) diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js index 65332ce308..e8fccb973d 100644 --- a/zeppelin-web/src/app/home/home.controller.js +++ b/zeppelin-web/src/app/home/home.controller.js @@ -12,135 +12,135 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl); +angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl) -function HomeCtrl($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv, +function HomeCtrl ($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv, ngToast, noteActionSrv, TRASH_FOLDER_ID) { - 'ngInject'; + 'ngInject' - ngToast.dismiss(); - var vm = this; - vm.notes = noteListDataFactory; - vm.websocketMsgSrv = websocketMsgSrv; - vm.arrayOrderingSrv = arrayOrderingSrv; + ngToast.dismiss() + let vm = this + vm.notes = noteListDataFactory + vm.websocketMsgSrv = websocketMsgSrv + vm.arrayOrderingSrv = arrayOrderingSrv - vm.notebookHome = false; - vm.noteCustomHome = true; + vm.notebookHome = false + vm.noteCustomHome = true if ($rootScope.ticket !== undefined) { - vm.staticHome = false; + vm.staticHome = false } else { - vm.staticHome = true; + vm.staticHome = true } - $scope.isReloading = false; - $scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID; - $scope.query = {q: ''}; + $scope.isReloading = false + $scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID + $scope.query = {q: ''} - $scope.initHome = function() { - websocketMsgSrv.getHomeNote(); - vm.noteCustomHome = false; - }; + $scope.initHome = function () { + websocketMsgSrv.getHomeNote() + vm.noteCustomHome = false + } - $scope.reloadNoteList = function() { - websocketMsgSrv.reloadAllNotesFromRepo(); - $scope.isReloadingNotes = true; - }; + $scope.reloadNoteList = function () { + websocketMsgSrv.reloadAllNotesFromRepo() + $scope.isReloadingNotes = true + } - $scope.toggleFolderNode = function(node) { - node.hidden = !node.hidden; - }; + $scope.toggleFolderNode = function (node) { + node.hidden = !node.hidden + } - angular.element('#loginModal').on('hidden.bs.modal', function(e) { - $rootScope.$broadcast('initLoginValues'); - }); + angular.element('#loginModal').on('hidden.bs.modal', function (e) { + $rootScope.$broadcast('initLoginValues') + }) /* ** $scope.$on functions below */ - $scope.$on('setNoteMenu', function(event, notes) { - $scope.isReloadingNotes = false; - }); + $scope.$on('setNoteMenu', function (event, notes) { + $scope.isReloadingNotes = false + }) - $scope.$on('setNoteContent', function(event, note) { + $scope.$on('setNoteContent', function (event, note) { if (vm.noteCustomHome) { - return; + return } if (note) { - vm.note = note; + vm.note = note // initialize look And Feel - $rootScope.$broadcast('setLookAndFeel', 'home'); + $rootScope.$broadcast('setLookAndFeel', 'home') // make it read only - vm.viewOnly = true; + vm.viewOnly = true - vm.notebookHome = true; - vm.staticHome = false; + vm.notebookHome = true + vm.staticHome = false } else { - vm.staticHome = true; - vm.notebookHome = false; + vm.staticHome = true + vm.notebookHome = false } - }); + }) - $scope.renameNote = function(nodeId, nodePath) { - noteActionSrv.renameNote(nodeId, nodePath); - }; + $scope.renameNote = function (nodeId, nodePath) { + noteActionSrv.renameNote(nodeId, nodePath) + } - $scope.moveNoteToTrash = function(noteId) { - noteActionSrv.moveNoteToTrash(noteId, false); - }; + $scope.moveNoteToTrash = function (noteId) { + noteActionSrv.moveNoteToTrash(noteId, false) + } - $scope.moveFolderToTrash = function(folderId) { - noteActionSrv.moveFolderToTrash(folderId); - }; + $scope.moveFolderToTrash = function (folderId) { + noteActionSrv.moveFolderToTrash(folderId) + } - $scope.restoreNote = function(noteId) { - websocketMsgSrv.restoreNote(noteId); - }; + $scope.restoreNote = function (noteId) { + websocketMsgSrv.restoreNote(noteId) + } - $scope.restoreFolder = function(folderId) { - websocketMsgSrv.restoreFolder(folderId); - }; + $scope.restoreFolder = function (folderId) { + websocketMsgSrv.restoreFolder(folderId) + } - $scope.restoreAll = function() { - noteActionSrv.restoreAll(); - }; + $scope.restoreAll = function () { + noteActionSrv.restoreAll() + } - $scope.renameFolder = function(node) { - noteActionSrv.renameFolder(node.id); - }; + $scope.renameFolder = function (node) { + noteActionSrv.renameFolder(node.id) + } - $scope.removeNote = function(noteId) { - noteActionSrv.removeNote(noteId, false); - }; + $scope.removeNote = function (noteId) { + noteActionSrv.removeNote(noteId, false) + } - $scope.removeFolder = function(folderId) { - noteActionSrv.removeFolder(folderId); - }; + $scope.removeFolder = function (folderId) { + noteActionSrv.removeFolder(folderId) + } - $scope.emptyTrash = function() { - noteActionSrv.emptyTrash(); - }; + $scope.emptyTrash = function () { + noteActionSrv.emptyTrash() + } - $scope.clearAllParagraphOutput = function(noteId) { - noteActionSrv.clearAllParagraphOutput(noteId); - }; + $scope.clearAllParagraphOutput = function (noteId) { + noteActionSrv.clearAllParagraphOutput(noteId) + } - $scope.isFilterNote = function(note) { + $scope.isFilterNote = function (note) { if (!$scope.query.q) { - return true; + return true } - var noteName = note.name; + let noteName = note.name if (noteName.toLowerCase().indexOf($scope.query.q.toLowerCase()) > -1) { - return true; + return true } - return false; - }; + return false + } - $scope.getNoteName = function(note) { - return arrayOrderingSrv.getNoteName(note); - }; + $scope.getNoteName = function (note) { + return arrayOrderingSrv.getNoteName(note) + } } diff --git a/zeppelin-web/src/app/interpreter/interpreter.controller.js b/zeppelin-web/src/app/interpreter/interpreter.controller.js index 7140345ec2..032f74c50e 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.controller.js +++ b/zeppelin-web/src/app/interpreter/interpreter.controller.js @@ -12,53 +12,53 @@ * limitations under the License. */ -import { ParagraphStatus, } from '../notebook/paragraph/paragraph.status'; +import { ParagraphStatus, } from '../notebook/paragraph/paragraph.status' -angular.module('zeppelinWebApp').controller('InterpreterCtrl', InterpreterCtrl); +angular.module('zeppelinWebApp').controller('InterpreterCtrl', InterpreterCtrl) -function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeout, $route) { - 'ngInject'; +function InterpreterCtrl ($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeout, $route) { + 'ngInject' - var interpreterSettingsTmp = []; - $scope.interpreterSettings = []; - $scope.availableInterpreters = {}; - $scope.showAddNewSetting = false; - $scope.showRepositoryInfo = false; - $scope.searchInterpreter = ''; - $scope._ = _; - $scope.interpreterPropertyWidgets = []; - ngToast.dismiss(); + var interpreterSettingsTmp = [] + $scope.interpreterSettings = [] + $scope.availableInterpreters = {} + $scope.showAddNewSetting = false + $scope.showRepositoryInfo = false + $scope.searchInterpreter = '' + $scope._ = _ + $scope.interpreterPropertyWidgets = [] + ngToast.dismiss() - $scope.openPermissions = function() { - $scope.showInterpreterAuth = true; - }; + $scope.openPermissions = function () { + $scope.showInterpreterAuth = true + } - $scope.closePermissions = function() { - $scope.showInterpreterAuth = false; - }; + $scope.closePermissions = function () { + $scope.showInterpreterAuth = false + } - var getSelectJson = function() { - var selectJson = { + let getSelectJson = function () { + let selectJson = { tags: false, multiple: true, tokenSeparators: [',', ' '], minimumInputLength: 2, ajax: { - url: function(params) { + url: function (params) { if (!params.term) { - return false; + return false } - return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term; + return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term }, delay: 250, - processResults: function(data, params) { - var users = []; + processResults: function (data, params) { + let users = [] if (data.body.users.length !== 0) { - for (var i = 0; i < data.body.users.length; i++) { + for (let i = 0; i < data.body.users.length; i++) { users.push({ 'id': data.body.users[i], 'text': data.body.users[i] - }); + }) } } return { @@ -66,365 +66,366 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou pagination: { more: false } - }; + } }, cache: false } - }; - return selectJson; - }; + } + return selectJson + } - $scope.togglePermissions = function(intpName) { - angular.element('#' + intpName + 'Users').select2(getSelectJson()); + $scope.togglePermissions = function (intpName) { + angular.element('#' + intpName + 'Users').select2(getSelectJson()) if ($scope.showInterpreterAuth) { - $scope.closePermissions(); + $scope.closePermissions() } else { - $scope.openPermissions(); + $scope.openPermissions() } - }; + } - $scope.$on('ngRenderFinished', function(event, data) { - for (var setting = 0; setting < $scope.interpreterSettings.length; setting++) { - angular.element('#' + $scope.interpreterSettings[setting].name + 'Users').select2(getSelectJson()); + $scope.$on('ngRenderFinished', function (event, data) { + for (let setting = 0; setting < $scope.interpreterSettings.length; setting++) { + angular.element('#' + $scope.interpreterSettings[setting].name + 'Users').select2(getSelectJson()) } - }); + }) - var getInterpreterSettings = function() { + let getInterpreterSettings = function () { $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting') - .success(function(data, status, headers, config) { - $scope.interpreterSettings = data.body; - checkDownloadingDependencies(); - }).error(function(data, status, headers, config) { - if (status === 401) { - ngToast.danger({ - content: 'You don\'t have permission on this page', - verticalPosition: 'bottom', - timeout: '3000' - }); - setTimeout(function() { - window.location.replace('/'); - }, 3000); - } - console.log('Error %o %o', status, data.message); - }); - }; + .success(function (data, status, headers, config) { + $scope.interpreterSettings = data.body + checkDownloadingDependencies() + }).error(function (data, status, headers, config) { + if (status === 401) { + ngToast.danger({ + content: 'You don\'t have permission on this page', + verticalPosition: 'bottom', + timeout: '3000' + }) + setTimeout(function () { + window.location.replace('/') + }, 3000) + } + console.log('Error %o %o', status, data.message) + }) + } - var checkDownloadingDependencies = function() { - var isDownloading = false; - for (var index = 0; index < $scope.interpreterSettings.length; index++) { - var setting = $scope.interpreterSettings[index]; + const checkDownloadingDependencies = function () { + let isDownloading = false + for (let index = 0; index < $scope.interpreterSettings.length; index++) { + let setting = $scope.interpreterSettings[index] if (setting.status === 'DOWNLOADING_DEPENDENCIES') { - isDownloading = true; + isDownloading = true } if (setting.status === ParagraphStatus.ERROR || setting.errorReason) { ngToast.danger({content: 'Error setting properties for interpreter \'' + setting.group + '.' + setting.name + '\': ' + setting.errorReason, - verticalPosition: 'top', dismissOnTimeout: false}); + verticalPosition: 'top', + dismissOnTimeout: false + }) } } if (isDownloading) { - $timeout(function() { + $timeout(function () { if ($route.current.$$route.originalPath === '/interpreter') { - getInterpreterSettings(); + getInterpreterSettings() } - }, 2000); + }, 2000) } - }; + } - var getAvailableInterpreters = function() { - $http.get(baseUrlSrv.getRestApiBase() + '/interpreter').success(function(data, status, headers, config) { - $scope.availableInterpreters = data.body; - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); - }; + let getAvailableInterpreters = function () { + $http.get(baseUrlSrv.getRestApiBase() + '/interpreter').success(function (data, status, headers, config) { + $scope.availableInterpreters = data.body + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) + } var getAvailableInterpreterPropertyWidgets = function () { $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/property/widgets') .success(function (data, status, headers, config) { - $scope.interpreterPropertyWidgets = data.body; + $scope.interpreterPropertyWidgets = data.body }).error(function (data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); - }; + console.log('Error %o %o', status, data.message) + }) + } - var emptyNewProperty = function(object) { + let emptyNewProperty = function(object) { angular.extend(object, {propertyValue: '', propertyKey: '', propertyWidget: $scope.interpreterPropertyWidgets[0]}); - }; + } - var emptyNewDependency = function(object) { - angular.extend(object, {depArtifact: '', depExclude: ''}); - }; + let emptyNewDependency = function (object) { + angular.extend(object, {depArtifact: '', depExclude: ''}) + } - var removeTMPSettings = function(index) { - interpreterSettingsTmp.splice(index, 1); - }; + let removeTMPSettings = function (index) { + interpreterSettingsTmp.splice(index, 1) + } - $scope.copyOriginInterpreterSettingProperties = function(settingId) { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - interpreterSettingsTmp[index] = angular.copy($scope.interpreterSettings[index]); - }; + $scope.copyOriginInterpreterSettingProperties = function (settingId) { + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + interpreterSettingsTmp[index] = angular.copy($scope.interpreterSettings[index]) + } - $scope.setPerNoteOption = function(settingId, sessionOption) { - var option; + $scope.setPerNoteOption = function (settingId, sessionOption) { + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } if (sessionOption === 'isolated') { - option.perNote = sessionOption; - option.session = false; - option.process = true; + option.perNote = sessionOption + option.session = false + option.process = true } else if (sessionOption === 'scoped') { - option.perNote = sessionOption; - option.session = true; - option.process = false; + option.perNote = sessionOption + option.session = true + option.process = false } else { - option.perNote = 'shared'; - option.session = false; - option.process = false; + option.perNote = 'shared' + option.session = false + option.process = false } - }; + } $scope.defaultValueByWidget = function(setting) { if (setting.propertyWidget === 'checkbox') { - setting.propertyValue = false; - return; + setting.propertyValue = false + return } - setting.propertyValue = ''; + setting.propertyValue = '' }; $scope.setPerUserOption = function(settingId, sessionOption) { - var option; + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } if (sessionOption === 'isolated') { - option.perUser = sessionOption; - option.session = false; - option.process = true; + option.perUser = sessionOption + option.session = false + option.process = true } else if (sessionOption === 'scoped') { - option.perUser = sessionOption; - option.session = true; - option.process = false; + option.perUser = sessionOption + option.session = true + option.process = false } else { - option.perUser = 'shared'; - option.session = false; - option.process = false; + option.perUser = 'shared' + option.session = false + option.process = false } - }; + } - $scope.getPerNoteOption = function(settingId) { - var option; + $scope.getPerNoteOption = function (settingId) { + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } if (option.perNote === 'scoped') { - return 'scoped'; + return 'scoped' } else if (option.perNote === 'isolated') { - return 'isolated'; + return 'isolated' } else { - return 'shared'; + return 'shared' } - }; + } - $scope.getPerUserOption = function(settingId) { - var option; + $scope.getPerUserOption = function (settingId) { + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } if (option.perUser === 'scoped') { - return 'scoped'; + return 'scoped' } else if (option.perUser === 'isolated') { - return 'isolated'; + return 'isolated' } else { - return 'shared'; + return 'shared' } - }; + } - $scope.getInterpreterRunningOption = function(settingId) { - var sharedModeName = 'shared'; + $scope.getInterpreterRunningOption = function (settingId) { + let sharedModeName = 'shared' - var globallyModeName = 'Globally'; - var perNoteModeName = 'Per Note'; - var perUserModeName = 'Per User'; + let globallyModeName = 'Globally' + let perNoteModeName = 'Per Note' + let perUserModeName = 'Per User' - var option; + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } - var perNote = option.perNote; - var perUser = option.perUser; + let perNote = option.perNote + let perUser = option.perUser // Globally == shared_perNote + shared_perUser if (perNote === sharedModeName && perUser === sharedModeName) { - return globallyModeName; + return globallyModeName } if ($rootScope.ticket.ticket === 'anonymous' && $rootScope.ticket.roles === '[]') { if (perNote !== undefined && typeof perNote === 'string' && perNote !== '') { - return perNoteModeName; + return perNoteModeName } } else if ($rootScope.ticket.ticket !== 'anonymous') { if (perNote !== undefined && typeof perNote === 'string' && perNote !== '') { if (perUser !== undefined && typeof perUser === 'string' && perUser !== '') { - return perUserModeName; + return perUserModeName } - return perNoteModeName; + return perNoteModeName } } - option.perNote = sharedModeName; - option.perUser = sharedModeName; - return globallyModeName; - }; + option.perNote = sharedModeName + option.perUser = sharedModeName + return globallyModeName + } - $scope.setInterpreterRunningOption = function(settingId, isPerNoteMode, isPerUserMode) { - var option; + $scope.setInterpreterRunningOption = function (settingId, isPerNoteMode, isPerUserMode) { + let option if (settingId === undefined) { - option = $scope.newInterpreterSetting.option; + option = $scope.newInterpreterSetting.option } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; - option = setting.option; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] + option = setting.option } - option.perNote = isPerNoteMode; - option.perUser = isPerUserMode; - }; + option.perNote = isPerNoteMode + option.perUser = isPerUserMode + } - $scope.updateInterpreterSetting = function(form, settingId) { - var thisConfirm = BootstrapDialog.confirm({ + $scope.updateInterpreterSetting = function (form, settingId) { + const thisConfirm = BootstrapDialog.confirm({ closable: false, closeByBackdrop: false, closeByKeyboard: false, title: '', message: 'Do you want to update this interpreter and restart with new settings?', - callback: function(result) { + callback: function (result) { if (result) { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] if (setting.propertyKey !== '' || setting.propertyKey) { - $scope.addNewInterpreterProperty(settingId); + $scope.addNewInterpreterProperty(settingId) } if (setting.depArtifact !== '' || setting.depArtifact) { - $scope.addNewInterpreterDependency(settingId); + $scope.addNewInterpreterDependency(settingId) } // add missing field of option if (!setting.option) { - setting.option = {}; + setting.option = {} } if (setting.option.isExistingProcess === undefined) { - setting.option.isExistingProcess = false; + setting.option.isExistingProcess = false } if (setting.option.setPermission === undefined) { - setting.option.setPermission = false; + setting.option.setPermission = false } if (setting.option.isUserImpersonate === undefined) { - setting.option.isUserImpersonate = false; + setting.option.isUserImpersonate = false } if (!($scope.getInterpreterRunningOption(settingId) === 'Per User' && $scope.getPerUserOption(settingId) === 'isolated')) { - setting.option.isUserImpersonate = false; + setting.option.isUserImpersonate = false } if (setting.option.remote === undefined) { // remote always true for now - setting.option.remote = true; + setting.option.remote = true } - setting.option.users = angular.element('#' + setting.name + 'Users').val(); + setting.option.users = angular.element('#' + setting.name + 'Users').val() - var request = { + let request = { option: angular.copy(setting.option), properties: angular.copy(setting.properties), dependencies: angular.copy(setting.dependencies) - }; + } - thisConfirm.$modalFooter.find('button').addClass('disabled'); + thisConfirm.$modalFooter.find('button').addClass('disabled') thisConfirm.$modalFooter.find('button:contains("OK")') - .html(' Saving Setting'); + .html(' Saving Setting') $http.put(baseUrlSrv.getRestApiBase() + '/interpreter/setting/' + settingId, request) - .success(function(data, status, headers, config) { - $scope.interpreterSettings[index] = data.body; - removeTMPSettings(index); - checkDownloadingDependencies(); - thisConfirm.close(); + .success(function (data, status, headers, config) { + $scope.interpreterSettings[index] = data.body + removeTMPSettings(index) + checkDownloadingDependencies() + thisConfirm.close() }) - .error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - ngToast.danger({content: data.message, verticalPosition: 'bottom'}); - form.$show(); - thisConfirm.close(); - }); - return false; + .error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + ngToast.danger({content: data.message, verticalPosition: 'bottom'}) + form.$show() + thisConfirm.close() + }) + return false } else { - form.$show(); + form.$show() } } - }); - }; + }) + } - $scope.resetInterpreterSetting = function(settingId) { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); + $scope.resetInterpreterSetting = function (settingId) { + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) // Set the old settings back - $scope.interpreterSettings[index] = angular.copy(interpreterSettingsTmp[index]); - removeTMPSettings(index); - }; + $scope.interpreterSettings[index] = angular.copy(interpreterSettingsTmp[index]) + removeTMPSettings(index) + } - $scope.removeInterpreterSetting = function(settingId) { + $scope.removeInterpreterSetting = function (settingId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Do you want to delete this interpreter setting?', - callback: function(result) { + callback: function (result) { if (result) { $http.delete(baseUrlSrv.getRestApiBase() + '/interpreter/setting/' + settingId) - .success(function(data, status, headers, config) { - - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - $scope.interpreterSettings.splice(index, 1); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); + .success(function (data, status, headers, config) { + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + $scope.interpreterSettings.splice(index, 1) + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) } } - }); - }; + }) + } - $scope.newInterpreterGroupChange = function() { - var el = _.pluck(_.filter($scope.availableInterpreters, {'name': $scope.newInterpreterSetting.group}), - 'properties'); - var properties = {}; - for (var i = 0; i < el.length; i++) { - var intpInfo = el[i]; - for (var key in intpInfo) { + $scope.newInterpreterGroupChange = function () { + let el = _.pluck(_.filter($scope.availableInterpreters, {'name': $scope.newInterpreterSetting.group}), + 'properties') + let properties = {} + for (let i = 0; i < el.length; i++) { + let intpInfo = el[i] + for (let key in intpInfo) { properties[key] = { value: intpInfo[key].defaultValue, description: intpInfo[key].description, @@ -432,41 +433,41 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou }; } } - $scope.newInterpreterSetting.properties = properties; - }; + $scope.newInterpreterSetting.properties = properties + } - $scope.restartInterpreterSetting = function(settingId) { + $scope.restartInterpreterSetting = function (settingId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Do you want to restart this interpreter?', - callback: function(result) { + callback: function (result) { if (result) { $http.put(baseUrlSrv.getRestApiBase() + '/interpreter/setting/restart/' + settingId) - .success(function(data, status, headers, config) { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - $scope.interpreterSettings[index] = data.body; - ngToast.info('Interpreter stopped. Will be lazily started on next run.'); - }).error(function(data, status, headers, config) { - var errorMsg = (data !== null) ? data.message : 'Could not connect to server.'; - console.log('Error %o %o', status, errorMsg); - ngToast.danger(errorMsg); - }); + .success(function (data, status, headers, config) { + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + $scope.interpreterSettings[index] = data.body + ngToast.info('Interpreter stopped. Will be lazily started on next run.') + }).error(function (data, status, headers, config) { + let errorMsg = (data !== null) ? data.message : 'Could not connect to server.' + console.log('Error %o %o', status, errorMsg) + ngToast.danger(errorMsg) + }) } } - }); - }; + }) + } - $scope.addNewInterpreterSetting = function() { - //user input validation on interpreter creation + $scope.addNewInterpreterSetting = function () { + // user input validation on interpreter creation if (!$scope.newInterpreterSetting.name || !$scope.newInterpreterSetting.name.trim() || !$scope.newInterpreterSetting.group) { BootstrapDialog.alert({ closable: true, title: 'Add interpreter', message: 'Please fill in interpreter name and choose a group' - }); - return; + }) + return } if ($scope.newInterpreterSetting.name.indexOf('.') >= 0) { @@ -474,8 +475,8 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou closable: true, title: 'Add interpreter', message: '\'.\' is invalid for interpreter name' - }); - return; + }) + return } if (_.findIndex($scope.interpreterSettings, {'name': $scope.newInterpreterSetting.name}) >= 0) { @@ -483,51 +484,51 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou closable: true, title: 'Add interpreter', message: 'Name ' + $scope.newInterpreterSetting.name + ' already exists' - }); - return; + }) + return } - var newSetting = $scope.newInterpreterSetting; + let newSetting = $scope.newInterpreterSetting if (newSetting.propertyKey !== '' || newSetting.propertyKey) { - $scope.addNewInterpreterProperty(); + $scope.addNewInterpreterProperty() } if (newSetting.depArtifact !== '' || newSetting.depArtifact) { - $scope.addNewInterpreterDependency(); + $scope.addNewInterpreterDependency() } if (newSetting.option.setPermission === undefined) { - newSetting.option.setPermission = false; + newSetting.option.setPermission = false } - newSetting.option.users = angular.element('#newInterpreterUsers').val(); + newSetting.option.users = angular.element('#newInterpreterUsers').val() - var request = angular.copy($scope.newInterpreterSetting); + let request = angular.copy($scope.newInterpreterSetting) // Change properties to proper request format - var newProperties = {}; + let newProperties = {} - for (var p in newSetting.properties) { - newProperties[p] = {value: newSetting.properties[p].value, widget: newSetting.properties[p].widget, name: p}; + for (let p in newSetting.properties) { + newProperties[p] = {value: newSetting.properties[p].value, widget: newSetting.properties[p].widget, name: p} } request.properties = newProperties; $http.post(baseUrlSrv.getRestApiBase() + '/interpreter/setting', request) - .success(function(data, status, headers, config) { - $scope.resetNewInterpreterSetting(); - getInterpreterSettings(); - $scope.showAddNewSetting = false; - checkDownloadingDependencies(); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - ngToast.danger({content: data.message, verticalPosition: 'bottom'}); - }); - }; + .success(function (data, status, headers, config) { + $scope.resetNewInterpreterSetting() + getInterpreterSettings() + $scope.showAddNewSetting = false + checkDownloadingDependencies() + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + ngToast.danger({content: data.message, verticalPosition: 'bottom'}) + }) + } - $scope.cancelInterpreterSetting = function() { - $scope.showAddNewSetting = false; - $scope.resetNewInterpreterSetting(); - }; + $scope.cancelInterpreterSetting = function () { + $scope.showAddNewSetting = false + $scope.resetNewInterpreterSetting() + } - $scope.resetNewInterpreterSetting = function() { + $scope.resetNewInterpreterSetting = function () { $scope.newInterpreterSetting = { name: undefined, group: undefined, @@ -541,113 +542,113 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou process: false } - }; - emptyNewProperty($scope.newInterpreterSetting); - }; - - $scope.removeInterpreterProperty = function(key, settingId) { - if (settingId === undefined) { - delete $scope.newInterpreterSetting.properties[key]; - } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - delete $scope.interpreterSettings[index].properties[key]; } - }; + emptyNewProperty($scope.newInterpreterSetting) + } - $scope.removeInterpreterDependency = function(artifact, settingId) { + $scope.removeInterpreterProperty = function (key, settingId) { + if (settingId === undefined) { + delete $scope.newInterpreterSetting.properties[key] + } else { + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + delete $scope.interpreterSettings[index].properties[key] + } + } + + $scope.removeInterpreterDependency = function (artifact, settingId) { if (settingId === undefined) { $scope.newInterpreterSetting.dependencies = _.reject($scope.newInterpreterSetting.dependencies, - function(el) { - return el.groupArtifactVersion === artifact; - }); + function (el) { + return el.groupArtifactVersion === artifact + }) } else { - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) $scope.interpreterSettings[index].dependencies = _.reject($scope.interpreterSettings[index].dependencies, - function(el) { - return el.groupArtifactVersion === artifact; - }); + function (el) { + return el.groupArtifactVersion === artifact + }) } - }; + } - $scope.addNewInterpreterProperty = function(settingId) { + $scope.addNewInterpreterProperty = function (settingId) { if (settingId === undefined) { // Add new property from create form if (!$scope.newInterpreterSetting.propertyKey || $scope.newInterpreterSetting.propertyKey === '') { - return; + return } $scope.newInterpreterSetting.properties[$scope.newInterpreterSetting.propertyKey] = { value: $scope.newInterpreterSetting.propertyValue, widget: $scope.newInterpreterSetting.propertyWidget }; - emptyNewProperty($scope.newInterpreterSetting); + emptyNewProperty($scope.newInterpreterSetting) } else { // Add new property from edit form - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] if (!setting.propertyKey || setting.propertyKey === '') { - return; + return } - setting.properties[setting.propertyKey] = {value: setting.propertyValue, widget: setting.propertyWidget}; + setting.properties[setting.propertyKey] = {value: setting.propertyValue, widget: setting.propertyWidget} emptyNewProperty(setting); } - }; + } - $scope.addNewInterpreterDependency = function(settingId) { + $scope.addNewInterpreterDependency = function (settingId) { if (settingId === undefined) { // Add new dependency from create form if (!$scope.newInterpreterSetting.depArtifact || $scope.newInterpreterSetting.depArtifact === '') { - return; + return } // overwrite if artifact already exists - var newSetting = $scope.newInterpreterSetting; - for (var d in newSetting.dependencies) { + let newSetting = $scope.newInterpreterSetting + for (let d in newSetting.dependencies) { if (newSetting.dependencies[d].groupArtifactVersion === newSetting.depArtifact) { newSetting.dependencies[d] = { 'groupArtifactVersion': newSetting.depArtifact, 'exclusions': newSetting.depExclude - }; - newSetting.dependencies.splice(d, 1); + } + newSetting.dependencies.splice(d, 1) } } newSetting.dependencies.push({ 'groupArtifactVersion': newSetting.depArtifact, 'exclusions': (newSetting.depExclude === '') ? [] : newSetting.depExclude - }); - emptyNewDependency(newSetting); + }) + emptyNewDependency(newSetting) } else { // Add new dependency from edit form - var index = _.findIndex($scope.interpreterSettings, {'id': settingId}); - var setting = $scope.interpreterSettings[index]; + let index = _.findIndex($scope.interpreterSettings, {'id': settingId}) + let setting = $scope.interpreterSettings[index] if (!setting.depArtifact || setting.depArtifact === '') { - return; + return } // overwrite if artifact already exists - for (var dep in setting.dependencies) { + for (let dep in setting.dependencies) { if (setting.dependencies[dep].groupArtifactVersion === setting.depArtifact) { setting.dependencies[dep] = { 'groupArtifactVersion': setting.depArtifact, 'exclusions': setting.depExclude - }; - setting.dependencies.splice(dep, 1); + } + setting.dependencies.splice(dep, 1) } } setting.dependencies.push({ 'groupArtifactVersion': setting.depArtifact, 'exclusions': (setting.depExclude === '') ? [] : setting.depExclude - }); - emptyNewDependency(setting); + }) + emptyNewDependency(setting) } - }; + } - $scope.resetNewRepositorySetting = function() { + $scope.resetNewRepositorySetting = function () { $scope.newRepoSetting = { id: '', url: '', @@ -659,92 +660,91 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou proxyPort: null, proxyLogin: '', proxyPassword: '' - }; - }; + } + } - var getRepositories = function() { + let getRepositories = function () { $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/repository') - .success(function(data, status, headers, config) { - $scope.repositories = data.body; - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); - }; + .success(function (data, status, headers, config) { + $scope.repositories = data.body + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) + } - $scope.addNewRepository = function() { - var request = angular.copy($scope.newRepoSetting); + $scope.addNewRepository = function () { + let request = angular.copy($scope.newRepoSetting) $http.post(baseUrlSrv.getRestApiBase() + '/interpreter/repository', request) - .success(function(data, status, headers, config) { - getRepositories(); - $scope.resetNewRepositorySetting(); - angular.element('#repoModal').modal('hide'); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', headers, config); - }); - }; + .success(function (data, status, headers, config) { + getRepositories() + $scope.resetNewRepositorySetting() + angular.element('#repoModal').modal('hide') + }).error(function (data, status, headers, config) { + console.log('Error %o %o', headers, config) + }) + } - $scope.removeRepository = function(repoId) { + $scope.removeRepository = function (repoId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Do you want to delete this repository?', - callback: function(result) { + callback: function (result) { if (result) { $http.delete(baseUrlSrv.getRestApiBase() + '/interpreter/repository/' + repoId) - .success(function(data, status, headers, config) { - var index = _.findIndex($scope.repositories, {'id': repoId}); - $scope.repositories.splice(index, 1); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); + .success(function (data, status, headers, config) { + let index = _.findIndex($scope.repositories, {'id': repoId}) + $scope.repositories.splice(index, 1) + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) } } - }); - }; + }) + } - $scope.isDefaultRepository = function(repoId) { + $scope.isDefaultRepository = function (repoId) { if (repoId === 'central' || repoId === 'local') { - return true; + return true } else { - return false; + return false } - }; + } - $scope.showErrorMessage = function(setting) { + $scope.showErrorMessage = function (setting) { BootstrapDialog.show({ title: 'Error downloading dependencies', message: setting.errorReason - }); - }; + }) + } - var init = function() { - getAvailableInterpreterPropertyWidgets(); + let init = function() { + getAvailableInterpreterPropertyWidgets() - $scope.resetNewInterpreterSetting(); - $scope.resetNewRepositorySetting(); + $scope.resetNewInterpreterSetting() + $scope.resetNewRepositorySetting() - getInterpreterSettings(); - getAvailableInterpreters(); - getRepositories(); - }; + getInterpreterSettings() + getAvailableInterpreters() + getRepositories() + } - $scope.showSparkUI = function(settingId) { + $scope.showSparkUI = function (settingId) { $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/getmetainfos/' + settingId + '?propName=url') - .success(function(data, status, headers, config) { - var url = data.body.url; + .success(function (data, status, headers, config) { + let url = data.body.url if (!url) { BootstrapDialog.alert({ message: 'No spark application running' - }); - return; + }) + return } - window.open(url, '_blank'); - }).error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); - }; + window.open(url, '_blank') + }).error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) + }) + } - init(); + init() } - diff --git a/zeppelin-web/src/app/interpreter/interpreter.filter.js b/zeppelin-web/src/app/interpreter/interpreter.filter.js index c5f1572b08..3f42572015 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.filter.js +++ b/zeppelin-web/src/app/interpreter/interpreter.filter.js @@ -12,11 +12,11 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').filter('sortByKey', sortByKey); +angular.module('zeppelinWebApp').filter('sortByKey', sortByKey) -function sortByKey() { - return function(properties) { - var sortedKeys = properties ? Object.keys(properties) : []; - return sortedKeys.sort(); - }; +function sortByKey () { + return function (properties) { + let sortedKeys = properties ? Object.keys(properties) : [] + return sortedKeys.sort() + } } diff --git a/zeppelin-web/src/app/jobmanager/jobmanager.controller.js b/zeppelin-web/src/app/jobmanager/jobmanager.controller.js index 5503be4324..ec6f1ab5d9 100644 --- a/zeppelin-web/src/app/jobmanager/jobmanager.controller.js +++ b/zeppelin-web/src/app/jobmanager/jobmanager.controller.js @@ -13,172 +13,169 @@ */ angular.module('zeppelinWebApp') - .controller('JobmanagerCtrl', JobmanagerCtrl); + .controller('JobmanagerCtrl', JobmanagerCtrl) -function JobmanagerCtrl($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) { - 'ngInject'; +function JobmanagerCtrl ($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) { + 'ngInject' - ngToast.dismiss(); - var asyncNotebookJobFilter = function(jobInfomations, filterConfig) { - return $q(function(resolve, reject) { - $scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig); - resolve($scope.JobInfomationsByFilter); - }); - }; + ngToast.dismiss() + let asyncNotebookJobFilter = function (jobInfomations, filterConfig) { + return $q(function (resolve, reject) { + $scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig) + resolve($scope.JobInfomationsByFilter) + }) + } - $scope.doFiltering = function(jobInfomations, filterConfig) { + $scope.doFiltering = function (jobInfomations, filterConfig) { asyncNotebookJobFilter(jobInfomations, filterConfig).then( - function() { + function () { // success - $scope.isLoadingFilter = false; + $scope.isLoadingFilter = false }, - function() { + function () { // failed - }); - }; + }) + } - $scope.filterValueToName = function(filterValue, maxStringLength) { + $scope.filterValueToName = function (filterValue, maxStringLength) { if ($scope.activeInterpreters === undefined) { - return; + return } - var index = _.findIndex($scope.activeInterpreters, {value: filterValue}); + let index = _.findIndex($scope.activeInterpreters, {value: filterValue}) if ($scope.activeInterpreters[index].name !== undefined) { if (maxStringLength !== undefined && maxStringLength > $scope.activeInterpreters[index].name) { - return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...'; + return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...' } - return $scope.activeInterpreters[index].name; + return $scope.activeInterpreters[index].name } else { - return 'Interpreter is not set'; + return 'Interpreter is not set' } - }; + } - $scope.setFilterValue = function(filterValue) { - $scope.filterConfig.filterValueInterpreter = filterValue; - $scope.doFiltering($scope.jobInfomations, $scope.filterConfig); - }; + $scope.setFilterValue = function (filterValue) { + $scope.filterConfig.filterValueInterpreter = filterValue + $scope.doFiltering($scope.jobInfomations, $scope.filterConfig) + } - $scope.onChangeRunJobToAlwaysTopToggle = function() { - $scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop; - $scope.doFiltering($scope.jobInfomations, $scope.filterConfig); - }; + $scope.onChangeRunJobToAlwaysTopToggle = function () { + $scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop + $scope.doFiltering($scope.jobInfomations, $scope.filterConfig) + } - $scope.onChangeSortAsc = function() { - $scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc; - $scope.doFiltering($scope.jobInfomations, $scope.filterConfig); - }; + $scope.onChangeSortAsc = function () { + $scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc + $scope.doFiltering($scope.jobInfomations, $scope.filterConfig) + } - $scope.doFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) { - var RETURN_KEY_CODE = 13; - $timeout.cancel($scope.dofilterTimeoutObject); - $scope.isActiveSearchTimer = true; - $scope.dofilterTimeoutObject = $timeout(function() { - $scope.doFiltering(jobInfomations, filterConfig); - $scope.isActiveSearchTimer = false; - }, 10000); + $scope.doFilterInputTyping = function (keyEvent, jobInfomations, filterConfig) { + let RETURN_KEY_CODE = 13 + $timeout.cancel($scope.dofilterTimeoutObject) + $scope.isActiveSearchTimer = true + $scope.dofilterTimeoutObject = $timeout(function () { + $scope.doFiltering(jobInfomations, filterConfig) + $scope.isActiveSearchTimer = false + }, 10000) if (keyEvent.which === RETURN_KEY_CODE) { - $timeout.cancel($scope.dofilterTimeoutObject); - $scope.doFiltering(jobInfomations, filterConfig); - $scope.isActiveSearchTimer = false; + $timeout.cancel($scope.dofilterTimeoutObject) + $scope.doFiltering(jobInfomations, filterConfig) + $scope.isActiveSearchTimer = false } - }; + } - $scope.doForceFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) { - $timeout.cancel($scope.dofilterTimeoutObject); - $scope.doFiltering(jobInfomations, filterConfig); - $scope.isActiveSearchTimer = false; - }; + $scope.doForceFilterInputTyping = function (keyEvent, jobInfomations, filterConfig) { + $timeout.cancel($scope.dofilterTimeoutObject) + $scope.doFiltering(jobInfomations, filterConfig) + $scope.isActiveSearchTimer = false + } - $scope.init = function() { - $scope.isLoadingFilter = true; - $scope.jobInfomations = []; - $scope.JobInfomationsByFilter = $scope.jobInfomations; + $scope.init = function () { + $scope.isLoadingFilter = true + $scope.jobInfomations = [] + $scope.JobInfomationsByFilter = $scope.jobInfomations $scope.filterConfig = { isRunningAlwaysTop: true, filterValueNotebookName: '', filterValueInterpreter: '*', isSortByAsc: true - }; - $scope.sortTooltipMsg = 'Switch to sort by desc'; - $scope.jobTypeFilter = jobManagerFilter; + } + $scope.sortTooltipMsg = 'Switch to sort by desc' + $scope.jobTypeFilter = jobManagerFilter - websocketMsgSrv.getNoteJobsList(); + websocketMsgSrv.getNoteJobsList() $scope.$watch('filterConfig.isSortByAsc', function (value) { if (value) { - $scope.sortTooltipMsg = 'Switch to sort by desc'; + $scope.sortTooltipMsg = 'Switch to sort by desc' } else { - $scope.sortTooltipMsg = 'Switch to sort by asc'; + $scope.sortTooltipMsg = 'Switch to sort by asc' } - }); + }) - $scope.$on('$destroy', function() { - websocketMsgSrv.unsubscribeJobManager(); - }); - }; + $scope.$on('$destroy', function () { + websocketMsgSrv.unsubscribeJobManager() + }) + } /* ** $scope.$on functions below */ - $scope.$on('setNoteJobs', function(event, responseData) { - $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime; - $scope.jobInfomations = responseData.jobs; - $scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {}; - $scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig); + $scope.$on('setNoteJobs', function (event, responseData) { + $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime + $scope.jobInfomations = responseData.jobs + $scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {} + $scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig) $scope.activeInterpreters = [ { name: 'ALL', value: '*' } - ]; - var interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false); - for (var index = 0, length = interpreterLists.length; index < length; index++) { + ] + let interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false) + for (let index = 0, length = interpreterLists.length; index < length; index++) { $scope.activeInterpreters.push({ name: interpreterLists[index], value: interpreterLists[index] - }); + }) } - $scope.doFiltering($scope.jobInfomations, $scope.filterConfig); - }); + $scope.doFiltering($scope.jobInfomations, $scope.filterConfig) + }) - $scope.$on('setUpdateNoteJobs', function(event, responseData) { - var jobInfomations = $scope.jobInfomations; - var indexStore = $scope.jobInfomationsIndexs; - $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime; - var notes = responseData.jobs; - notes.map(function(changedItem) { + $scope.$on('setUpdateNoteJobs', function (event, responseData) { + let jobInfomations = $scope.jobInfomations + let indexStore = $scope.jobInfomationsIndexs + $scope.lastJobServerUnixTime = responseData.lastResponseUnixTime + let notes = responseData.jobs + notes.map(function (changedItem) { if (indexStore[changedItem.noteId] === undefined) { - var newItem = angular.copy(changedItem); - jobInfomations.push(newItem); - indexStore[changedItem.noteId] = newItem; + let newItem = angular.copy(changedItem) + jobInfomations.push(newItem) + indexStore[changedItem.noteId] = newItem } else { - var changeOriginTarget = indexStore[changedItem.noteId]; + let changeOriginTarget = indexStore[changedItem.noteId] if (changedItem.isRemoved !== undefined && changedItem.isRemoved === true) { - // remove Item. - var removeIndex = _.findIndex(indexStore, changedItem.noteId); + let removeIndex = _.findIndex(indexStore, changedItem.noteId) if (removeIndex > -1) { - indexStore.splice(removeIndex, 1); + indexStore.splice(removeIndex, 1) } - removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId}); + removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId}) if (removeIndex) { - jobInfomations.splice(removeIndex, 1); + jobInfomations.splice(removeIndex, 1) } - } else { // change value for item. - changeOriginTarget.isRunningJob = changedItem.isRunningJob; - changeOriginTarget.noteName = changedItem.noteName; - changeOriginTarget.noteType = changedItem.noteType; - changeOriginTarget.interpreter = changedItem.interpreter; - changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun; - changeOriginTarget.paragraphs = changedItem.paragraphs; + changeOriginTarget.isRunningJob = changedItem.isRunningJob + changeOriginTarget.noteName = changedItem.noteName + changeOriginTarget.noteType = changedItem.noteType + changeOriginTarget.interpreter = changedItem.interpreter + changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun + changeOriginTarget.paragraphs = changedItem.paragraphs } } - }); - $scope.doFiltering(jobInfomations, $scope.filterConfig); - }); + }) + $scope.doFiltering(jobInfomations, $scope.filterConfig) + }) } - diff --git a/zeppelin-web/src/app/jobmanager/jobmanager.filter.js b/zeppelin-web/src/app/jobmanager/jobmanager.filter.js index 05a1b6ecc1..92114984ee 100644 --- a/zeppelin-web/src/app/jobmanager/jobmanager.filter.js +++ b/zeppelin-web/src/app/jobmanager/jobmanager.filter.js @@ -12,37 +12,36 @@ * limitations under the License. */ -angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter); +angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter) -function jobManagerFilter() { - function filterContext(jobItems, filterConfig) { - var filterValueInterpreter = filterConfig.filterValueInterpreter; - var filterValueNotebookName = filterConfig.filterValueNotebookName; - var isSortByAsc = filterConfig.isSortByAsc; - var filterItems = jobItems; +function jobManagerFilter () { + function filterContext (jobItems, filterConfig) { + let filterValueInterpreter = filterConfig.filterValueInterpreter + let filterValueNotebookName = filterConfig.filterValueNotebookName + let isSortByAsc = filterConfig.isSortByAsc + let filterItems = jobItems if (filterValueInterpreter === undefined) { - filterItems = _.filter(filterItems, function(jobItem) { - return jobItem.interpreter === undefined ? true : false; - }); + filterItems = _.filter(filterItems, function (jobItem) { + return jobItem.interpreter === undefined ? true : false + }) } else if (filterValueInterpreter !== '*') { - filterItems = _.where(filterItems, {interpreter: filterValueInterpreter}); + filterItems = _.where(filterItems, {interpreter: filterValueInterpreter}) } if (filterValueNotebookName !== '') { - filterItems = _.filter(filterItems, function(jobItem) { - var lowerFilterValue = filterValueNotebookName.toLocaleLowerCase(); - var lowerNotebookName = jobItem.noteName.toLocaleLowerCase(); - return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*')); - }); + filterItems = _.filter(filterItems, function (jobItem) { + let lowerFilterValue = filterValueNotebookName.toLocaleLowerCase() + let lowerNotebookName = jobItem.noteName.toLocaleLowerCase() + return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*')) + }) } - filterItems = _.sortBy(filterItems, function(sortItem) { - return sortItem.noteName.toLowerCase(); - }); + filterItems = _.sortBy(filterItems, function (sortItem) { + return sortItem.noteName.toLowerCase() + }) - return isSortByAsc ? filterItems : filterItems.reverse(); + return isSortByAsc ? filterItems : filterItems.reverse() } - return filterContext; + return filterContext } - diff --git a/zeppelin-web/src/app/jobmanager/jobs/job.controller.js b/zeppelin-web/src/app/jobmanager/jobs/job.controller.js index 50f8d7afbe..e1ce02ddc2 100644 --- a/zeppelin-web/src/app/jobmanager/jobs/job.controller.js +++ b/zeppelin-web/src/app/jobmanager/jobs/job.controller.js @@ -12,38 +12,38 @@ * limitations under the License. */ -import { ParagraphStatus, } from '../../notebook/paragraph/paragraph.status'; +import { ParagraphStatus, } from '../../notebook/paragraph/paragraph.status' -angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl); +angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl) -function JobCtrl($scope, $http, baseUrlSrv) { - 'ngInject'; +function JobCtrl ($scope, $http, baseUrlSrv) { + 'ngInject' - $scope.init = function(jobInformation) { - $scope.progressValue = 0; - }; + $scope.init = function (jobInformation) { + $scope.progressValue = 0 + } - $scope.getProgress = function() { - var statusList = _.pluck($scope.notebookJob.paragraphs, 'status'); - var runningJob = _.countBy(statusList, function(status) { + $scope.getProgress = function () { + let statusList = _.pluck($scope.notebookJob.paragraphs, 'status') + let runningJob = _.countBy(statusList, function (status) { if (status === ParagraphStatus.RUNNING || status === ParagraphStatus.FINISHED) { - return 'matchCount'; + return 'matchCount' } else { - return 'none'; + return 'none' } - }); - var totalCount = statusList.length; - var runningJobCount = runningJob.matchCount; - var result = Math.ceil(runningJobCount / totalCount * 100); - return isNaN(result) ? 0 : result; - }; + }) + let totalCount = statusList.length + let runningJobCount = runningJob.matchCount + let result = Math.ceil(runningJobCount / totalCount * 100) + return isNaN(result) ? 0 : result + } - $scope.runNotebookJob = function(notebookId) { + $scope.runNotebookJob = function (notebookId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Run all paragraphs?', - callback: function(result) { + callback: function (result) { if (result) { $http({ method: 'POST', @@ -51,30 +51,31 @@ function JobCtrl($scope, $http, baseUrlSrv) { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - }).then(function successCallback(response) { + }).then(function successCallback (response) { // success - }, function errorCallback(errorResponse) { - var errorText = 'SERVER ERROR'; + }, function errorCallback (errorResponse) { + let errorText = 'SERVER ERROR' + // eslint-disable-next-line no-extra-boolean-cast if (!!errorResponse.data.message) { - errorText = errorResponse.data.message; + errorText = errorResponse.data.message } BootstrapDialog.alert({ closable: true, title: 'Execution Failure', message: errorText - }); - }); + }) + }) } } - }); - }; + }) + } - $scope.stopNotebookJob = function(notebookId) { + $scope.stopNotebookJob = function (notebookId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Stop all paragraphs?', - callback: function(result) { + callback: function (result) { if (result) { $http({ method: 'DELETE', @@ -82,28 +83,26 @@ function JobCtrl($scope, $http, baseUrlSrv) { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - }).then(function successCallback(response) { + }).then(function successCallback (response) { // success - }, function errorCallback(errorResponse) { - var errorText = 'SERVER ERROR'; + }, function errorCallback (errorResponse) { + let errorText = 'SERVER ERROR' + // eslint-disable-next-line no-extra-boolean-cast if (!!errorResponse.data.message) { - - errorText = errorResponse.data.message; + errorText = errorResponse.data.message } BootstrapDialog.alert({ closable: true, title: 'Stop Failure', message: errorText - }); - }); + }) + }) } } - }); - }; - - $scope.lastExecuteTime = function(unixtime) { - return moment.unix(unixtime / 1000).fromNow(); - }; + }) + } + $scope.lastExecuteTime = function (unixtime) { + return moment.unix(unixtime / 1000).fromNow() + } } - diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 5fde0f49c4..e481e9be4c 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -12,25 +12,25 @@ * limitations under the License. */ -import { isParagraphRunning, } from './paragraph/paragraph.status'; +import { isParagraphRunning, } from './paragraph/paragraph.status' -angular.module('zeppelinWebApp').controller('NotebookCtrl', NotebookCtrl); +angular.module('zeppelinWebApp').controller('NotebookCtrl', NotebookCtrl) -function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, +function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, $http, websocketMsgSrv, baseUrlSrv, $timeout, saveAsService, ngToast, noteActionSrv, noteVarShareService, TRASH_FOLDER_ID, heliumService) { - 'ngInject'; + 'ngInject' - ngToast.dismiss(); + ngToast.dismiss() - $scope.note = null; - $scope.moment = moment; - $scope.editorToggled = false; - $scope.tableToggled = false; - $scope.viewOnly = false; - $scope.showSetting = false; - $scope.looknfeelOption = ['default', 'simple', 'report']; + $scope.note = null + $scope.moment = moment + $scope.editorToggled = false + $scope.tableToggled = false + $scope.viewOnly = false + $scope.showSetting = false + $scope.looknfeelOption = ['default', 'simple', 'report'] $scope.cronOption = [ {name: 'None', value: undefined}, {name: '1m', value: '0 0/1 * * * ?'}, @@ -40,50 +40,50 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, {name: '6h', value: '0 0 0/6 * * ?'}, {name: '12h', value: '0 0 0/12 * * ?'}, {name: '1d', value: '0 0 0 * * ?'} - ]; + ] - $scope.interpreterSettings = []; - $scope.interpreterBindings = []; - $scope.isNoteDirty = null; - $scope.saveTimer = null; + $scope.interpreterSettings = [] + $scope.interpreterBindings = [] + $scope.isNoteDirty = null + $scope.saveTimer = null - var connectedOnce = false; - var isRevisionPath = function(path) { - var pattern = new RegExp('^.*\/notebook\/[a-zA-Z0-9_]*\/revision\/[a-zA-Z0-9_]*'); - return pattern.test(path); - }; + let connectedOnce = false + let isRevisionPath = function (path) { + let pattern = new RegExp('^.*\/notebook\/[a-zA-Z0-9_]*\/revision\/[a-zA-Z0-9_]*') + return pattern.test(path) + } - $scope.noteRevisions = []; - $scope.currentRevision = 'Head'; - $scope.revisionView = isRevisionPath($location.path()); + $scope.noteRevisions = [] + $scope.currentRevision = 'Head' + $scope.revisionView = isRevisionPath($location.path()) - $scope.$on('setConnectedStatus', function(event, param) { + $scope.$on('setConnectedStatus', function (event, param) { if (connectedOnce && param) { - initNotebook(); + initNotebook() } - connectedOnce = true; - }); + connectedOnce = true + }) - $scope.getCronOptionNameFromValue = function(value) { + $scope.getCronOptionNameFromValue = function (value) { if (!value) { - return ''; + return '' } - for (var o in $scope.cronOption) { + for (let o in $scope.cronOption) { if ($scope.cronOption[o].value === value) { - return $scope.cronOption[o].name; + return $scope.cronOption[o].name } } - return value; - }; + return value + } - $scope.blockAnonUsers = function() { - var zeppelinVersion = $rootScope.zeppelinVersion; - var url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/security/notebook_authorization.html'; - var content = 'Only authenticated user can set the permission.' + + $scope.blockAnonUsers = function () { + let zeppelinVersion = $rootScope.zeppelinVersion + let url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/security/notebook_authorization.html' + let content = 'Only authenticated user can set the permission.' + '' + '' + - ''; + '' BootstrapDialog.show({ closable: false, closeByBackdrop: false, @@ -92,187 +92,189 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, message: content, buttons: [{ label: 'Close', - action: function(dialog) { - dialog.close(); + action: function (dialog) { + dialog.close() } }] - }); - }; + }) + } /** Init the new controller */ - var initNotebook = function() { - noteVarShareService.clear(); + const initNotebook = function () { + noteVarShareService.clear() if ($routeParams.revisionId) { - websocketMsgSrv.getNoteByRevision($routeParams.noteId, $routeParams.revisionId); + websocketMsgSrv.getNoteByRevision($routeParams.noteId, $routeParams.revisionId) } else { - websocketMsgSrv.getNote($routeParams.noteId); + websocketMsgSrv.getNote($routeParams.noteId) } - websocketMsgSrv.listRevisionHistory($routeParams.noteId); - var currentRoute = $route.current; + websocketMsgSrv.listRevisionHistory($routeParams.noteId) + let currentRoute = $route.current if (currentRoute) { setTimeout( - function() { - var routeParams = currentRoute.params; - var $id = angular.element('#' + routeParams.paragraph + '_container'); + function () { + let routeParams = currentRoute.params + let $id = angular.element('#' + routeParams.paragraph + '_container') if ($id.length > 0) { // adjust for navbar - var top = $id.offset().top - 103; - angular.element('html, body').scrollTo({top: top, left: 0}); + let top = $id.offset().top - 103 + angular.element('html, body').scrollTo({top: top, left: 0}) } }, 1000 - ); + ) } - }; + } - initNotebook(); + initNotebook() - $scope.focusParagraphOnClick = function(clickEvent) { + $scope.focusParagraphOnClick = function (clickEvent) { if (!$scope.note) { - return; + return } - for (var i = 0; i < $scope.note.paragraphs.length; i++) { - var paragraphId = $scope.note.paragraphs[i].id; + for (let i = 0; i < $scope.note.paragraphs.length; i++) { + let paragraphId = $scope.note.paragraphs[i].id if (jQuery.contains(angular.element('#' + paragraphId + '_container')[0], clickEvent.target)) { - $scope.$broadcast('focusParagraph', paragraphId, 0, true); - break; + $scope.$broadcast('focusParagraph', paragraphId, 0, true) + break } } - }; + } // register mouseevent handler for focus paragraph - document.addEventListener('click', $scope.focusParagraphOnClick); + document.addEventListener('click', $scope.focusParagraphOnClick) - $scope.keyboardShortcut = function(keyEvent) { + $scope.keyboardShortcut = function (keyEvent) { // handle keyevent if (!$scope.viewOnly && !$scope.revisionView) { - $scope.$broadcast('keyEvent', keyEvent); + $scope.$broadcast('keyEvent', keyEvent) } - }; + } // register mouseevent handler for focus paragraph - document.addEventListener('keydown', $scope.keyboardShortcut); + document.addEventListener('keydown', $scope.keyboardShortcut) - $scope.paragraphOnDoubleClick = function(paragraphId) { - $scope.$broadcast('doubleClickParagraph', paragraphId); - }; + $scope.paragraphOnDoubleClick = function (paragraphId) { + $scope.$broadcast('doubleClickParagraph', paragraphId) + } // Move the note to trash and go back to the main page - $scope.moveNoteToTrash = function(noteId) { - noteActionSrv.moveNoteToTrash(noteId, true); - }; + $scope.moveNoteToTrash = function (noteId) { + noteActionSrv.moveNoteToTrash(noteId, true) + } // Remove the note permanently if it's in the trash - $scope.removeNote = function(noteId) { - noteActionSrv.removeNote(noteId, true); - }; + $scope.removeNote = function (noteId) { + noteActionSrv.removeNote(noteId, true) + } - $scope.isTrash = function(note) { - return note ? note.name.split('/')[0] === TRASH_FOLDER_ID : false; - }; + $scope.isTrash = function (note) { + return note ? note.name.split('/')[0] === TRASH_FOLDER_ID : false + } - //Export notebook - $scope.exportNote = function() { - var jsonContent = JSON.stringify($scope.note); - saveAsService.saveAs(jsonContent, $scope.note.name, 'json'); - }; + // Export notebook + $scope.exportNote = function () { + let jsonContent = JSON.stringify($scope.note) + saveAsService.saveAs(jsonContent, $scope.note.name, 'json') + } - //Clone note - $scope.cloneNote = function(noteId) { + // Clone note + $scope.cloneNote = function (noteId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Do you want to clone this note?', - callback: function(result) { + callback: function (result) { if (result) { - websocketMsgSrv.cloneNote(noteId); - $location.path('/'); + websocketMsgSrv.cloneNote(noteId) + $location.path('/') } } - }); - }; + }) + } // checkpoint/commit notebook - $scope.checkpointNote = function(commitMessage) { + $scope.checkpointNote = function (commitMessage) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Commit note to current repository?', - callback: function(result) { + callback: function (result) { if (result) { - websocketMsgSrv.checkpointNote($routeParams.noteId, commitMessage); + websocketMsgSrv.checkpointNote($routeParams.noteId, commitMessage) } } - }); - document.getElementById('note.checkpoint.message').value = ''; - }; + }) + document.getElementById('note.checkpoint.message').value = '' + } // set notebook head to given revision - $scope.setNoteRevision = function() { + $scope.setNoteRevision = function () { BootstrapDialog.confirm({ closable: true, title: '', message: 'Set notebook head to current revision?', - callback: function(result) { + callback: function (result) { if (result) { - websocketMsgSrv.setNoteRevision($routeParams.noteId, $routeParams.revisionId); + websocketMsgSrv.setNoteRevision($routeParams.noteId, $routeParams.revisionId) } } - }); - }; + }) + } - $scope.$on('listRevisionHistory', function(event, data) { - console.log('received list of revisions %o', data); - $scope.noteRevisions = data.revisionList; + $scope.$on('listRevisionHistory', function (event, data) { + console.log('received list of revisions %o', data) + $scope.noteRevisions = data.revisionList $scope.noteRevisions.splice(0, 0, { id: 'Head', message: 'Head' - }); + }) if ($routeParams.revisionId) { - var index = _.findIndex($scope.noteRevisions, {'id': $routeParams.revisionId}); + let index = _.findIndex($scope.noteRevisions, {'id': $routeParams.revisionId}) if (index > -1) { - $scope.currentRevision = $scope.noteRevisions[index].message; + $scope.currentRevision = $scope.noteRevisions[index].message } } - }); + }) - $scope.$on('noteRevision', function(event, data) { - console.log('received note revision %o', data); + $scope.$on('noteRevision', function (event, data) { + console.log('received note revision %o', data) if (data.note) { - $scope.note = data.note; - initializeLookAndFeel(); + $scope.note = data.note + initializeLookAndFeel() } else { - $location.path('/'); + $location.path('/') } - }); + }) - $scope.$on('setNoteRevisionResult', function(event, data) { - console.log('received set note revision result %o', data); + $scope.$on('setNoteRevisionResult', function (event, data) { + console.log('received set note revision result %o', data) if (data.status) { - $location.path('/notebook/' + $routeParams.noteId); + $location.path('/notebook/' + $routeParams.noteId) } - }); + }) - $scope.visitRevision = function(revision) { + $scope.visitRevision = function (revision) { if (revision.id) { if (revision.id === 'Head') { - $location.path('/notebook/' + $routeParams.noteId); + $location.path('/notebook/' + $routeParams.noteId) } else { - $location.path('/notebook/' + $routeParams.noteId + '/revision/' + revision.id); + $location.path('/notebook/' + $routeParams.noteId + '/revision/' + revision.id) } } else { ngToast.danger({content: 'There is a problem with this Revision', - verticalPosition: 'top', dismissOnTimeout: false}); + verticalPosition: 'top', + dismissOnTimeout: false + }) } - }; + } - $scope.runAllParagraphs = function(noteId) { + $scope.runAllParagraphs = function (noteId) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Run all paragraphs?', - callback: function(result) { + callback: function (result) { if (result) { const paragraphs = $scope.note.paragraphs.map(p => { return { @@ -281,491 +283,491 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, paragraph: p.text, config: p.config, params: p.settings.params - }; - }); - websocketMsgSrv.runAllParagraphs(noteId, paragraphs); + } + }) + websocketMsgSrv.runAllParagraphs(noteId, paragraphs) } } - }); - }; + }) + } - $scope.saveNote = function() { + $scope.saveNote = function () { if ($scope.note && $scope.note.paragraphs) { - _.forEach($scope.note.paragraphs, function(par) { + _.forEach($scope.note.paragraphs, function (par) { angular .element('#' + par.id + '_paragraphColumn_main') .scope() - .saveParagraph(par); - }); - $scope.isNoteDirty = null; + .saveParagraph(par) + }) + $scope.isNoteDirty = null } - }; + } - $scope.clearAllParagraphOutput = function(noteId) { - noteActionSrv.clearAllParagraphOutput(noteId); - }; + $scope.clearAllParagraphOutput = function (noteId) { + noteActionSrv.clearAllParagraphOutput(noteId) + } - $scope.toggleAllEditor = function() { + $scope.toggleAllEditor = function () { if ($scope.editorToggled) { - $scope.$broadcast('openEditor'); + $scope.$broadcast('openEditor') } else { - $scope.$broadcast('closeEditor'); + $scope.$broadcast('closeEditor') } - $scope.editorToggled = !$scope.editorToggled; - }; + $scope.editorToggled = !$scope.editorToggled + } - $scope.showAllEditor = function() { - $scope.$broadcast('openEditor'); - }; + $scope.showAllEditor = function () { + $scope.$broadcast('openEditor') + } - $scope.hideAllEditor = function() { - $scope.$broadcast('closeEditor'); - }; + $scope.hideAllEditor = function () { + $scope.$broadcast('closeEditor') + } - $scope.toggleAllTable = function() { + $scope.toggleAllTable = function () { if ($scope.tableToggled) { - $scope.$broadcast('openTable'); + $scope.$broadcast('openTable') } else { - $scope.$broadcast('closeTable'); + $scope.$broadcast('closeTable') } - $scope.tableToggled = !$scope.tableToggled; - }; + $scope.tableToggled = !$scope.tableToggled + } - $scope.showAllTable = function() { - $scope.$broadcast('openTable'); - }; + $scope.showAllTable = function () { + $scope.$broadcast('openTable') + } - $scope.hideAllTable = function() { - $scope.$broadcast('closeTable'); - }; + $scope.hideAllTable = function () { + $scope.$broadcast('closeTable') + } /** * @returns {boolean} true if one more paragraphs are running. otherwise return false. */ - $scope.isNoteRunning = function() { - if (!$scope.note) { return false; } + $scope.isNoteRunning = function () { + if (!$scope.note) { return false } for (let i = 0; i < $scope.note.paragraphs.length; i++) { if (isParagraphRunning($scope.note.paragraphs[i])) { - return true; + return true } } - return false; - }; + return false + } - $scope.killSaveTimer = function() { + $scope.killSaveTimer = function () { if ($scope.saveTimer) { - $timeout.cancel($scope.saveTimer); - $scope.saveTimer = null; + $timeout.cancel($scope.saveTimer) + $scope.saveTimer = null } - }; + } - $scope.startSaveTimer = function() { - $scope.killSaveTimer(); - $scope.isNoteDirty = true; - //console.log('startSaveTimer called ' + $scope.note.id); - $scope.saveTimer = $timeout(function() { - $scope.saveNote(); - }, 10000); - }; + $scope.startSaveTimer = function () { + $scope.killSaveTimer() + $scope.isNoteDirty = true + // console.log('startSaveTimer called ' + $scope.note.id); + $scope.saveTimer = $timeout(function () { + $scope.saveNote() + }, 10000) + } - angular.element(window).on('beforeunload', function(e) { - $scope.killSaveTimer(); - $scope.saveNote(); - }); + angular.element(window).on('beforeunload', function (e) { + $scope.killSaveTimer() + $scope.saveNote() + }) - $scope.setLookAndFeel = function(looknfeel) { - $scope.note.config.looknfeel = looknfeel; + $scope.setLookAndFeel = function (looknfeel) { + $scope.note.config.looknfeel = looknfeel if ($scope.revisionView === true) { - $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel); + $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel) } else { - $scope.setConfig(); + $scope.setConfig() } - }; + } /** Set cron expression for this note **/ - $scope.setCronScheduler = function(cronExpr) { + $scope.setCronScheduler = function (cronExpr) { if (cronExpr) { if (!$scope.note.config.cronExecutingUser) { - $scope.note.config.cronExecutingUser = $rootScope.ticket.principal; + $scope.note.config.cronExecutingUser = $rootScope.ticket.principal } } else { - $scope.note.config.cronExecutingUser = ''; + $scope.note.config.cronExecutingUser = '' } - $scope.note.config.cron = cronExpr; - $scope.setConfig(); - }; + $scope.note.config.cron = cronExpr + $scope.setConfig() + } /** Set the username of the user to be used to execute all notes in notebook **/ - $scope.setCronExecutingUser = function(cronExecutingUser) { - $scope.note.config.cronExecutingUser = cronExecutingUser; - $scope.setConfig(); - }; + $scope.setCronExecutingUser = function (cronExecutingUser) { + $scope.note.config.cronExecutingUser = cronExecutingUser + $scope.setConfig() + } /** Set release resource for this note **/ - $scope.setReleaseResource = function(value) { - $scope.note.config.releaseresource = value; - $scope.setConfig(); - }; + $scope.setReleaseResource = function (value) { + $scope.note.config.releaseresource = value + $scope.setConfig() + } /** Update note config **/ - $scope.setConfig = function(config) { + $scope.setConfig = function (config) { if (config) { - $scope.note.config = config; + $scope.note.config = config } - websocketMsgSrv.updateNote($scope.note.id, $scope.note.name, $scope.note.config); - }; + websocketMsgSrv.updateNote($scope.note.id, $scope.note.name, $scope.note.config) + } /** Update the note name */ - $scope.updateNoteName = function(newName) { - const trimmedNewName = newName.trim(); + $scope.updateNoteName = function (newName) { + const trimmedNewName = newName.trim() if (trimmedNewName.length > 0 && $scope.note.name !== trimmedNewName) { - $scope.note.name = trimmedNewName; - websocketMsgSrv.renameNote($scope.note.id, $scope.note.name); + $scope.note.name = trimmedNewName + websocketMsgSrv.renameNote($scope.note.id, $scope.note.name) } - }; + } - var initializeLookAndFeel = function() { + const initializeLookAndFeel = function () { if (!$scope.note.config.looknfeel) { - $scope.note.config.looknfeel = 'default'; + $scope.note.config.looknfeel = 'default' } else { - $scope.viewOnly = $scope.note.config.looknfeel === 'report' ? true : false; + $scope.viewOnly = $scope.note.config.looknfeel === 'report' ? true : false } if ($scope.note.paragraphs && $scope.note.paragraphs[0]) { - $scope.note.paragraphs[0].focus = true; + $scope.note.paragraphs[0].focus = true } - $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel); - }; + $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel) + } - var cleanParagraphExcept = function(paragraphId, note) { - var noteCopy = {}; - noteCopy.id = note.id; - noteCopy.name = note.name; - noteCopy.config = note.config; - noteCopy.info = note.info; - noteCopy.paragraphs = []; - for (var i = 0; i < note.paragraphs.length; i++) { + let cleanParagraphExcept = function (paragraphId, note) { + let noteCopy = {} + noteCopy.id = note.id + noteCopy.name = note.name + noteCopy.config = note.config + noteCopy.info = note.info + noteCopy.paragraphs = [] + for (let i = 0; i < note.paragraphs.length; i++) { if (note.paragraphs[i].id === paragraphId) { - noteCopy.paragraphs[0] = note.paragraphs[i]; + noteCopy.paragraphs[0] = note.paragraphs[i] if (!noteCopy.paragraphs[0].config) { - noteCopy.paragraphs[0].config = {}; + noteCopy.paragraphs[0].config = {} } - noteCopy.paragraphs[0].config.editorHide = true; - noteCopy.paragraphs[0].config.tableHide = false; - break; + noteCopy.paragraphs[0].config.editorHide = true + noteCopy.paragraphs[0].config.tableHide = false + break } } - return noteCopy; - }; + return noteCopy + } - var addPara = function(paragraph, index) { - $scope.note.paragraphs.splice(index, 0, paragraph); - _.each($scope.note.paragraphs, function(para) { + let addPara = function (paragraph, index) { + $scope.note.paragraphs.splice(index, 0, paragraph) + _.each($scope.note.paragraphs, function (para) { if (para.id === paragraph.id) { - para.focus = true; - $scope.$broadcast('focusParagraph', para.id, 0, false); + para.focus = true + $scope.$broadcast('focusParagraph', para.id, 0, false) } - }); - }; + }) + } - var removePara = function(paragraphId) { - var removeIdx; - _.each($scope.note.paragraphs, function(para, idx) { + let removePara = function (paragraphId) { + let removeIdx + _.each($scope.note.paragraphs, function (para, idx) { if (para.id === paragraphId) { - removeIdx = idx; + removeIdx = idx } - }); - return $scope.note.paragraphs.splice(removeIdx, 1); - }; + }) + return $scope.note.paragraphs.splice(removeIdx, 1) + } - $scope.$on('addParagraph', function(event, paragraph, index) { + $scope.$on('addParagraph', function (event, paragraph, index) { if ($scope.paragraphUrl) { - return; + return } - addPara(paragraph, index); - }); + addPara(paragraph, index) + }) - $scope.$on('removeParagraph', function(event, paragraphId) { + $scope.$on('removeParagraph', function (event, paragraphId) { if ($scope.paragraphUrl) { - return; + return } - removePara(paragraphId); - }); + removePara(paragraphId) + }) - $scope.$on('moveParagraph', function(event, paragraphId, newIdx) { - var removedPara = removePara(paragraphId); + $scope.$on('moveParagraph', function (event, paragraphId, newIdx) { + let removedPara = removePara(paragraphId) if (removedPara && removedPara.length === 1) { - addPara(removedPara[0], newIdx); + addPara(removedPara[0], newIdx) } - }); + }) - $scope.$on('updateNote', function(event, name, config, info) { + $scope.$on('updateNote', function (event, name, config, info) { /** update Note name */ if (name !== $scope.note.name) { - console.log('change note name to : %o', $scope.note.name); - $scope.note.name = name; + console.log('change note name to : %o', $scope.note.name) + $scope.note.name = name } - $scope.note.config = config; - $scope.note.info = info; - initializeLookAndFeel(); - }); + $scope.note.config = config + $scope.note.info = info + initializeLookAndFeel() + }) - var getInterpreterBindings = function() { - websocketMsgSrv.getInterpreterBindings($scope.note.id); - }; + let getInterpreterBindings = function () { + websocketMsgSrv.getInterpreterBindings($scope.note.id) + } - $scope.$on('interpreterBindings', function(event, data) { - $scope.interpreterBindings = data.interpreterBindings; - $scope.interpreterBindingsOrig = angular.copy($scope.interpreterBindings); // to check dirty + $scope.$on('interpreterBindings', function (event, data) { + $scope.interpreterBindings = data.interpreterBindings + $scope.interpreterBindingsOrig = angular.copy($scope.interpreterBindings) // to check dirty - var selected = false; - var key; - var setting; + let selected = false + let key + let setting for (key in $scope.interpreterBindings) { - setting = $scope.interpreterBindings[key]; + setting = $scope.interpreterBindings[key] if (setting.selected) { - selected = true; - break; + selected = true + break } } if (!selected) { // make default selection - var selectedIntp = {}; + let selectedIntp = {} for (key in $scope.interpreterBindings) { - setting = $scope.interpreterBindings[key]; + setting = $scope.interpreterBindings[key] if (!selectedIntp[setting.name]) { - setting.selected = true; - selectedIntp[setting.name] = true; + setting.selected = true + selectedIntp[setting.name] = true } } - $scope.showSetting = true; + $scope.showSetting = true } - }); + }) $scope.interpreterSelectionListeners = { - accept: function(sourceItemHandleScope, destSortableScope) {return true;}, - itemMoved: function(event) {}, - orderChanged: function(event) {} - }; + accept: function (sourceItemHandleScope, destSortableScope) { return true }, + itemMoved: function (event) {}, + orderChanged: function (event) {} + } - $scope.openSetting = function() { - $scope.showSetting = true; - getInterpreterBindings(); - }; + $scope.openSetting = function () { + $scope.showSetting = true + getInterpreterBindings() + } - $scope.closeSetting = function() { + $scope.closeSetting = function () { if (isSettingDirty()) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Interpreter setting changes will be discarded.', - callback: function(result) { + callback: function (result) { if (result) { - $scope.$apply(function() { - $scope.showSetting = false; - }); + $scope.$apply(function () { + $scope.showSetting = false + }) } } - }); + }) } else { - $scope.showSetting = false; + $scope.showSetting = false } - }; + } - $scope.saveSetting = function() { - var selectedSettingIds = []; - for (var no in $scope.interpreterBindings) { - var setting = $scope.interpreterBindings[no]; + $scope.saveSetting = function () { + let selectedSettingIds = [] + for (let no in $scope.interpreterBindings) { + let setting = $scope.interpreterBindings[no] if (setting.selected) { - selectedSettingIds.push(setting.id); + selectedSettingIds.push(setting.id) } } - websocketMsgSrv.saveInterpreterBindings($scope.note.id, selectedSettingIds); - console.log('Interpreter bindings %o saved', selectedSettingIds); + websocketMsgSrv.saveInterpreterBindings($scope.note.id, selectedSettingIds) + console.log('Interpreter bindings %o saved', selectedSettingIds) - _.forEach($scope.note.paragraphs, function(n, key) { - var regExp = /^\s*%/g; + _.forEach($scope.note.paragraphs, function (n, key) { + let regExp = /^\s*%/g if (n.text && !regExp.exec(n.text)) { - $scope.$broadcast('saveInterpreterBindings', n.id); + $scope.$broadcast('saveInterpreterBindings', n.id) } - }); + }) - $scope.showSetting = false; - }; + $scope.showSetting = false + } - $scope.toggleSetting = function() { + $scope.toggleSetting = function () { if ($scope.showSetting) { - $scope.closeSetting(); + $scope.closeSetting() } else { - $scope.openSetting(); - $scope.closePermissions(); - angular.element('html, body').animate({ scrollTop: 0 }, 'slow'); + $scope.openSetting() + $scope.closePermissions() + angular.element('html, body').animate({ scrollTop: 0 }, 'slow') } - }; + } - var getPermissions = function(callback) { - $http.get(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions'). - success(function(data, status, headers, config) { - $scope.permissions = data.body; - $scope.permissionsOrig = angular.copy($scope.permissions); // to check dirty + let getPermissions = function (callback) { + $http.get(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions') + .success(function (data, status, headers, config) { + $scope.permissions = data.body + $scope.permissionsOrig = angular.copy($scope.permissions) // to check dirty - var selectJson = { + let selectJson = { tokenSeparators: [',', ' '], ajax: { - url: function(params) { + url: function (params) { if (!params.term) { - return false; + return false } - return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term; + return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term }, delay: 250, - processResults: function(data, params) { - var results = []; + processResults: function (data, params) { + let results = [] if (data.body.users.length !== 0) { - var users = []; - for (var len = 0; len < data.body.users.length; len++) { + let users = [] + for (let len = 0; len < data.body.users.length; len++) { users.push({ 'id': data.body.users[len], 'text': data.body.users[len] - }); + }) } results.push({ 'text': 'Users :', 'children': users - }); + }) } if (data.body.roles.length !== 0) { - var roles = []; - for (var len = 0; len < data.body.roles.length; len++) { + let roles = [] + for (let len = 0; len < data.body.roles.length; len++) { roles.push({ 'id': data.body.roles[len], 'text': data.body.roles[len] - }); + }) } results.push({ 'text': 'Roles :', 'children': roles - }); + }) } return { results: results, pagination: { more: false } - }; + } }, cache: false }, width: ' ', tags: true, minimumInputLength: 3 - }; + } - $scope.setIamOwner(); - angular.element('#selectOwners').select2(selectJson); - angular.element('#selectReaders').select2(selectJson); - angular.element('#selectWriters').select2(selectJson); + $scope.setIamOwner() + angular.element('#selectOwners').select2(selectJson) + angular.element('#selectReaders').select2(selectJson) + angular.element('#selectWriters').select2(selectJson) if (callback) { - callback(); + callback() } - }). - error(function(data, status, headers, config) { + }) + .error(function (data, status, headers, config) { if (status !== 0) { - console.log('Error %o %o', status, data.message); + console.log('Error %o %o', status, data.message) } - }); - }; + }) + } - $scope.openPermissions = function() { - $scope.showPermissions = true; - getPermissions(); - }; + $scope.openPermissions = function () { + $scope.showPermissions = true + getPermissions() + } - $scope.closePermissions = function() { + $scope.closePermissions = function () { if (isPermissionsDirty()) { BootstrapDialog.confirm({ closable: true, title: '', message: 'Changes will be discarded.', - callback: function(result) { + callback: function (result) { if (result) { - $scope.$apply(function() { - $scope.showPermissions = false; - }); + $scope.$apply(function () { + $scope.showPermissions = false + }) } } - }); + }) } else { - $scope.showPermissions = false; + $scope.showPermissions = false } - }; - - function convertPermissionsToArray() { - $scope.permissions.owners = angular.element('#selectOwners').val(); - $scope.permissions.readers = angular.element('#selectReaders').val(); - $scope.permissions.writers = angular.element('#selectWriters').val(); - angular.element('.permissionsForm select').find('option:not([is-select2="false"])').remove(); } - $scope.restartInterpreter = function(interpeter) { - var thisConfirm = BootstrapDialog.confirm({ + function convertPermissionsToArray () { + $scope.permissions.owners = angular.element('#selectOwners').val() + $scope.permissions.readers = angular.element('#selectReaders').val() + $scope.permissions.writers = angular.element('#selectWriters').val() + angular.element('.permissionsForm select').find('option:not([is-select2="false"])').remove() + } + + $scope.restartInterpreter = function(interpreter) { + const thisConfirm = BootstrapDialog.confirm({ closable: false, closeByBackdrop: false, closeByKeyboard: false, title: '', - message: 'Do you want to restart ' + interpeter.name + ' interpreter?', + message: 'Do you want to restart ' + interpreter.name + ' interpreter?', callback: function(result) { if (result) { - var payload = { + let payload = { 'noteId': $scope.note.id - }; + } - thisConfirm.$modalFooter.find('button').addClass('disabled'); + thisConfirm.$modalFooter.find('button').addClass('disabled') thisConfirm.$modalFooter.find('button:contains("OK")') - .html(' Saving Setting'); + .html(' Saving Setting') - $http.put(baseUrlSrv.getRestApiBase() + '/interpreter/setting/restart/' + interpeter.id, payload) + $http.put(baseUrlSrv.getRestApiBase() + '/interpreter/setting/restart/' + interpreter.id, payload) .success(function(data, status, headers, config) { - var index = _.findIndex($scope.interpreterSettings, {'id': interpeter.id}); - $scope.interpreterSettings[index] = data.body; - thisConfirm.close(); - }).error(function(data, status, headers, config) { - thisConfirm.close(); - console.log('Error %o %o', status, data.message); - BootstrapDialog.show({ - title: 'Error restart interpreter.', - message: data.message - }); - }); - return false; + let index = _.findIndex($scope.interpreterSettings, {'id': interpreter.id}) + $scope.interpreterSettings[index] = data.body + thisConfirm.close() + }).error(function (data, status, headers, config) { + thisConfirm.close() + console.log('Error %o %o', status, data.message) + BootstrapDialog.show({ + title: 'Error restart interpreter.', + message: data.message + }) + }) + return false } } - }); - }; + }) + } - $scope.savePermissions = function() { - convertPermissionsToArray(); + $scope.savePermissions = function () { + convertPermissionsToArray() $http.put(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions', - $scope.permissions, {withCredentials: true}). - success(function(data, status, headers, config) { - getPermissions(function() { - console.log('Note permissions %o saved', $scope.permissions); + $scope.permissions, {withCredentials: true}) + .success(function (data, status, headers, config) { + getPermissions(function () { + console.log('Note permissions %o saved', $scope.permissions) BootstrapDialog.alert({ closable: true, title: 'Permissions Saved Successfully!!!', message: 'Owners : ' + $scope.permissions.owners + '\n\n' + 'Readers : ' + $scope.permissions.readers + '\n\n' + 'Writers : ' + $scope.permissions.writers - }); - $scope.showPermissions = false; - }); - }). - error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); + }) + $scope.showPermissions = false + }) + }) + .error(function (data, status, headers, config) { + console.log('Error %o %o', status, data.message) BootstrapDialog.show({ closable: false, closeByBackdrop: false, @@ -775,249 +777,249 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, buttons: [ { label: 'Login', - action: function(dialog) { - dialog.close(); + action: function (dialog) { + dialog.close() angular.element('#loginModal').modal({ show: 'true' - }); + }) } }, { label: 'Cancel', - action: function(dialog) { - dialog.close(); - $location.path('/'); + action: function (dialog) { + dialog.close() + $location.path('/') } } ] - }); - }); - }; + }) + }) + } - $scope.togglePermissions = function() { - var principal = $rootScope.ticket.principal; - $scope.isAnonymous = principal === 'anonymous' ? true : false; + $scope.togglePermissions = function () { + let principal = $rootScope.ticket.principal + $scope.isAnonymous = principal === 'anonymous' ? true : false if (!!principal && $scope.isAnonymous) { - $scope.blockAnonUsers(); + $scope.blockAnonUsers() } else { if ($scope.showPermissions) { - $scope.closePermissions(); - angular.element('#selectOwners').select2({}); - angular.element('#selectReaders').select2({}); - angular.element('#selectWriters').select2({}); + $scope.closePermissions() + angular.element('#selectOwners').select2({}) + angular.element('#selectReaders').select2({}) + angular.element('#selectWriters').select2({}) } else { - $scope.openPermissions(); - $scope.closeSetting(); + $scope.openPermissions() + $scope.closeSetting() } } - }; + } - $scope.setIamOwner = function() { + $scope.setIamOwner = function () { if ($scope.permissions.owners.length > 0 && _.indexOf($scope.permissions.owners, $rootScope.ticket.principal) < 0) { - $scope.isOwner = false; - return false; + $scope.isOwner = false + return false } - $scope.isOwner = true; - return true; - }; + $scope.isOwner = true + return true + } - $scope.toggleNotePersonalizedMode = function() { - var personalizedMode = $scope.note.config.personalizedMode; + $scope.toggleNotePersonalizedMode = function () { + let personalizedMode = $scope.note.config.personalizedMode if ($scope.isOwner) { BootstrapDialog.confirm({ closable: true, title: 'Setting the result display', - message: function(dialog) { - var modeText = $scope.note.config.personalizedMode === 'true' ? 'collaborate' : 'personalize'; - return 'Do you want to ' + modeText + ' your analysis?'; + message: function (dialog) { + let modeText = $scope.note.config.personalizedMode === 'true' ? 'collaborate' : 'personalize' + return 'Do you want to ' + modeText + ' your analysis?' }, - callback: function(result) { + callback: function (result) { if (result) { if ($scope.note.config.personalizedMode === undefined) { - $scope.note.config.personalizedMode = 'false'; + $scope.note.config.personalizedMode = 'false' } - $scope.note.config.personalizedMode = personalizedMode === 'true' ? 'false' : 'true'; - websocketMsgSrv.updatePersonalizedMode($scope.note.id, $scope.note.config.personalizedMode); + $scope.note.config.personalizedMode = personalizedMode === 'true' ? 'false' : 'true' + websocketMsgSrv.updatePersonalizedMode($scope.note.id, $scope.note.config.personalizedMode) } } - }); + }) } - }; + } - var isSettingDirty = function() { + const isSettingDirty = function () { if (angular.equals($scope.interpreterBindings, $scope.interpreterBindingsOrig)) { - return false; + return false } else { - return true; + return true } - }; + } - var isPermissionsDirty = function() { + const isPermissionsDirty = function () { if (angular.equals($scope.permissions, $scope.permissionsOrig)) { - return false; + return false } else { - return true; + return true } - }; + } - angular.element(document).click(function() { - angular.element('.ace_autocomplete').hide(); - }); + angular.element(document).click(function () { + angular.element('.ace_autocomplete').hide() + }) /* ** $scope.$on functions below */ - $scope.$on('setConnectedStatus', function(event, param) { + $scope.$on('setConnectedStatus', function (event, param) { if (connectedOnce && param) { - initNotebook(); + initNotebook() } - connectedOnce = true; - }); + connectedOnce = true + }) - $scope.$on('moveParagraphUp', function(event, paragraph) { - var newIndex = -1; - for (var i = 0; i < $scope.note.paragraphs.length; i++) { + $scope.$on('moveParagraphUp', function (event, paragraph) { + let newIndex = -1 + for (let i = 0; i < $scope.note.paragraphs.length; i++) { if ($scope.note.paragraphs[i].id === paragraph.id) { - newIndex = i - 1; - break; + newIndex = i - 1 + break } } if (newIndex < 0 || newIndex >= $scope.note.paragraphs.length) { - return; + return } // save dirtyText of moving paragraphs. - var prevParagraph = $scope.note.paragraphs[newIndex]; + let prevParagraph = $scope.note.paragraphs[newIndex] angular .element('#' + paragraph.id + '_paragraphColumn_main') .scope() - .saveParagraph(paragraph); + .saveParagraph(paragraph) angular .element('#' + prevParagraph.id + '_paragraphColumn_main') .scope() - .saveParagraph(prevParagraph); - websocketMsgSrv.moveParagraph(paragraph.id, newIndex); - }); + .saveParagraph(prevParagraph) + websocketMsgSrv.moveParagraph(paragraph.id, newIndex) + }) - $scope.$on('moveParagraphDown', function(event, paragraph) { - var newIndex = -1; - for (var i = 0; i < $scope.note.paragraphs.length; i++) { + $scope.$on('moveParagraphDown', function (event, paragraph) { + let newIndex = -1 + for (let i = 0; i < $scope.note.paragraphs.length; i++) { if ($scope.note.paragraphs[i].id === paragraph.id) { - newIndex = i + 1; - break; + newIndex = i + 1 + break } } if (newIndex < 0 || newIndex >= $scope.note.paragraphs.length) { - return; + return } // save dirtyText of moving paragraphs. - var nextParagraph = $scope.note.paragraphs[newIndex]; + let nextParagraph = $scope.note.paragraphs[newIndex] angular .element('#' + paragraph.id + '_paragraphColumn_main') .scope() - .saveParagraph(paragraph); + .saveParagraph(paragraph) angular .element('#' + nextParagraph.id + '_paragraphColumn_main') .scope() - .saveParagraph(nextParagraph); - websocketMsgSrv.moveParagraph(paragraph.id, newIndex); - }); + .saveParagraph(nextParagraph) + websocketMsgSrv.moveParagraph(paragraph.id, newIndex) + }) - $scope.$on('moveFocusToPreviousParagraph', function(event, currentParagraphId) { - var focus = false; - for (var i = $scope.note.paragraphs.length - 1; i >= 0; i--) { + $scope.$on('moveFocusToPreviousParagraph', function (event, currentParagraphId) { + let focus = false + for (let i = $scope.note.paragraphs.length - 1; i >= 0; i--) { if (focus === false) { if ($scope.note.paragraphs[i].id === currentParagraphId) { - focus = true; - continue; + focus = true + continue } } else { - $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, -1); - break; + $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, -1) + break } } - }); + }) - $scope.$on('moveFocusToNextParagraph', function(event, currentParagraphId) { - var focus = false; - for (var i = 0; i < $scope.note.paragraphs.length; i++) { + $scope.$on('moveFocusToNextParagraph', function (event, currentParagraphId) { + let focus = false + for (let i = 0; i < $scope.note.paragraphs.length; i++) { if (focus === false) { if ($scope.note.paragraphs[i].id === currentParagraphId) { - focus = true; - continue; + focus = true + continue } } else { - $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, 0); - break; + $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, 0) + break } } - }); + }) - $scope.$on('insertParagraph', function(event, paragraphId, position) { - var newIndex = -1; - for (var i = 0; i < $scope.note.paragraphs.length; i++) { + $scope.$on('insertParagraph', function (event, paragraphId, position) { + let newIndex = -1 + for (let i = 0; i < $scope.note.paragraphs.length; i++) { if ($scope.note.paragraphs[i].id === paragraphId) { - //determine position of where to add new paragraph; default is below + // determine position of where to add new paragraph; default is below if (position === 'above') { - newIndex = i; + newIndex = i } else { - newIndex = i + 1; + newIndex = i + 1 } - break; + break } } if (newIndex < 0 || newIndex > $scope.note.paragraphs.length) { - return; + return } - websocketMsgSrv.insertParagraph(newIndex); - }); + websocketMsgSrv.insertParagraph(newIndex) + }) - $scope.$on('setNoteContent', function(event, note) { + $scope.$on('setNoteContent', function (event, note) { if (note === undefined) { - $location.path('/'); + $location.path('/') } - $scope.note = note; + $scope.note = note - $scope.paragraphUrl = $routeParams.paragraphId; - $scope.asIframe = $routeParams.asIframe; + $scope.paragraphUrl = $routeParams.paragraphId + $scope.asIframe = $routeParams.asIframe if ($scope.paragraphUrl) { - $scope.note = cleanParagraphExcept($scope.paragraphUrl, $scope.note); - $scope.$broadcast('$unBindKeyEvent', $scope.$unBindKeyEvent); - $rootScope.$broadcast('setIframe', $scope.asIframe); - initializeLookAndFeel(); - return; + $scope.note = cleanParagraphExcept($scope.paragraphUrl, $scope.note) + $scope.$broadcast('$unBindKeyEvent', $scope.$unBindKeyEvent) + $rootScope.$broadcast('setIframe', $scope.asIframe) + initializeLookAndFeel() + return } - initializeLookAndFeel(); + initializeLookAndFeel() - //open interpreter binding setting when there're none selected - getInterpreterBindings(); - getPermissions(); - var isPersonalized = $scope.note.config.personalizedMode; - isPersonalized = isPersonalized === undefined ? 'false' : isPersonalized; - $scope.note.config.personalizedMode = isPersonalized; - }); + // open interpreter binding setting when there're none selected + getInterpreterBindings() + getPermissions() + let isPersonalized = $scope.note.config.personalizedMode + isPersonalized = isPersonalized === undefined ? 'false' : isPersonalized + $scope.note.config.personalizedMode = isPersonalized + }) - $scope.$on('$destroy', function() { - angular.element(window).off('beforeunload'); - $scope.killSaveTimer(); - $scope.saveNote(); + $scope.$on('$destroy', function () { + angular.element(window).off('beforeunload') + $scope.killSaveTimer() + $scope.saveNote() - document.removeEventListener('click', $scope.focusParagraphOnClick); - document.removeEventListener('keydown', $scope.keyboardShortcut); - }); + document.removeEventListener('click', $scope.focusParagraphOnClick) + document.removeEventListener('keydown', $scope.keyboardShortcut) + }) - $scope.$on('$unBindKeyEvent', function() { - document.removeEventListener('click', $scope.focusParagraphOnClick); - document.removeEventListener('keydown', $scope.keyboardShortcut); - }); + $scope.$on('$unBindKeyEvent', function () { + document.removeEventListener('click', $scope.focusParagraphOnClick) + document.removeEventListener('keydown', $scope.keyboardShortcut) + }) - angular.element(window).bind('resize', function() { - const actionbarHeight = document.getElementById('actionbar').lastElementChild.clientHeight; - angular.element(document.getElementById('content')).css('padding-top', actionbarHeight - 20); - }); + angular.element(window).bind('resize', function () { + const actionbarHeight = document.getElementById('actionbar').lastElementChild.clientHeight + angular.element(document.getElementById('content')).css('padding-top', actionbarHeight - 20) + }) } diff --git a/zeppelin-web/src/app/notebook/notebook.controller.test.js b/zeppelin-web/src/app/notebook/notebook.controller.test.js index 6df6893aa0..f393d2c09c 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.test.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.test.js @@ -1,139 +1,139 @@ -describe('Controller: NotebookCtrl', function() { - beforeEach(angular.mock.module('zeppelinWebApp')); +describe('Controller: NotebookCtrl', function () { + beforeEach(angular.mock.module('zeppelinWebApp')) - var scope; + let scope - var websocketMsgSrvMock = { - getNote: function() {}, - listRevisionHistory: function() {}, - getInterpreterBindings: function() {}, - updateNote: function() {}, - renameNote: function() {} - }; + let websocketMsgSrvMock = { + getNote: function () {}, + listRevisionHistory: function () {}, + getInterpreterBindings: function () {}, + updateNote: function () {}, + renameNote: function () {} + } - var baseUrlSrvMock = { - getRestApiBase: function() { - return 'http://localhost:8080'; + let baseUrlSrvMock = { + getRestApiBase: function () { + return 'http://localhost:8080' } - }; + } - var noteMock = { + let noteMock = { id: 1, name: 'my note', config: {}, - }; + } - beforeEach(inject(function($controller, $rootScope) { - scope = $rootScope.$new(); + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new() $controller('NotebookCtrl', { $scope: scope, websocketMsgSrv: websocketMsgSrvMock, baseUrlSrv: baseUrlSrvMock - }); - })); + }) + })) - beforeEach(function() { - scope.note = noteMock; - }); + beforeEach(function () { + scope.note = noteMock + }) - var functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor', + let functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor', 'showAllEditor', 'hideAllEditor', 'toggleAllTable', 'hideAllTable', 'showAllTable', 'isNoteRunning', 'killSaveTimer', 'startSaveTimer', 'setLookAndFeel', 'setCronScheduler', 'setConfig', 'updateNoteName', - 'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting']; + 'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting'] - functions.forEach(function(fn) { - it('check for scope functions to be defined : ' + fn, function() { - expect(scope[fn]).toBeDefined(); - }); - }); + functions.forEach(function (fn) { + it('check for scope functions to be defined : ' + fn, function () { + expect(scope[fn]).toBeDefined() + }) + }) - it('should set default value of "editorToggled" to false', function() { - expect(scope.editorToggled).toEqual(false); - }); + it('should set default value of "editorToggled" to false', function () { + expect(scope.editorToggled).toEqual(false) + }) - it('should set "showSetting" to true when openSetting() is called', function() { - scope.openSetting(); - expect(scope.showSetting).toEqual(true); - }); + it('should set "showSetting" to true when openSetting() is called', function () { + scope.openSetting() + expect(scope.showSetting).toEqual(true) + }) - it('should set "showSetting" to false when closeSetting() is called', function() { - scope.closeSetting(); - expect(scope.showSetting).toEqual(false); - }); + it('should set "showSetting" to false when closeSetting() is called', function () { + scope.closeSetting() + expect(scope.showSetting).toEqual(false) + }) - it('should return the correct value for getCronOptionNameFromValue()', function() { - var none = scope.getCronOptionNameFromValue(); - var oneMin = scope.getCronOptionNameFromValue('0 0/1 * * * ?'); - var fiveMin = scope.getCronOptionNameFromValue('0 0/5 * * * ?'); - var oneHour = scope.getCronOptionNameFromValue('0 0 0/1 * * ?'); - var threeHours = scope.getCronOptionNameFromValue('0 0 0/3 * * ?'); - var sixHours = scope.getCronOptionNameFromValue('0 0 0/6 * * ?'); - var twelveHours = scope.getCronOptionNameFromValue('0 0 0/12 * * ?'); - var oneDay = scope.getCronOptionNameFromValue('0 0 0 * * ?'); + it('should return the correct value for getCronOptionNameFromValue()', function () { + let none = scope.getCronOptionNameFromValue() + let oneMin = scope.getCronOptionNameFromValue('0 0/1 * * * ?') + let fiveMin = scope.getCronOptionNameFromValue('0 0/5 * * * ?') + let oneHour = scope.getCronOptionNameFromValue('0 0 0/1 * * ?') + let threeHours = scope.getCronOptionNameFromValue('0 0 0/3 * * ?') + let sixHours = scope.getCronOptionNameFromValue('0 0 0/6 * * ?') + let twelveHours = scope.getCronOptionNameFromValue('0 0 0/12 * * ?') + let oneDay = scope.getCronOptionNameFromValue('0 0 0 * * ?') - expect(none).toEqual(''); - expect(oneMin).toEqual('1m'); - expect(fiveMin).toEqual('5m'); - expect(oneHour).toEqual('1h'); - expect(threeHours).toEqual('3h'); - expect(sixHours).toEqual('6h'); - expect(twelveHours).toEqual('12h'); - expect(oneDay).toEqual('1d'); - }); + expect(none).toEqual('') + expect(oneMin).toEqual('1m') + expect(fiveMin).toEqual('5m') + expect(oneHour).toEqual('1h') + expect(threeHours).toEqual('3h') + expect(sixHours).toEqual('6h') + expect(twelveHours).toEqual('12h') + expect(oneDay).toEqual('1d') + }) - it('should have "isNoteDirty" as null by default', function() { - expect(scope.isNoteDirty).toEqual(null); - }); + it('should have "isNoteDirty" as null by default', function () { + expect(scope.isNoteDirty).toEqual(null) + }) - it('should first call killSaveTimer() when calling startSaveTimer()', function() { - expect(scope.saveTimer).toEqual(null); - spyOn(scope, 'killSaveTimer'); - scope.startSaveTimer(); - expect(scope.killSaveTimer).toHaveBeenCalled(); - }); + it('should first call killSaveTimer() when calling startSaveTimer()', function () { + expect(scope.saveTimer).toEqual(null) + spyOn(scope, 'killSaveTimer') + scope.startSaveTimer() + expect(scope.killSaveTimer).toHaveBeenCalled() + }) - it('should set "saveTimer" when saveTimer() and killSaveTimer() are called', function() { - expect(scope.saveTimer).toEqual(null); - scope.startSaveTimer(); - expect(scope.saveTimer).toBeTruthy(); - scope.killSaveTimer(); - expect(scope.saveTimer).toEqual(null); - }); + it('should set "saveTimer" when saveTimer() and killSaveTimer() are called', function () { + expect(scope.saveTimer).toEqual(null) + scope.startSaveTimer() + expect(scope.saveTimer).toBeTruthy() + scope.killSaveTimer() + expect(scope.saveTimer).toEqual(null) + }) - it('should NOT update note name when updateNoteName() is called with an invalid name', function() { - spyOn(websocketMsgSrvMock, 'renameNote'); - scope.updateNoteName(''); - expect(scope.note.name).toEqual(noteMock.name); - expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled(); - scope.updateNoteName(' '); - expect(scope.note.name).toEqual(noteMock.name); - expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled(); - scope.updateNoteName(scope.note.name); - expect(scope.note.name).toEqual(noteMock.name); - expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled(); - }); + it('should NOT update note name when updateNoteName() is called with an invalid name', function () { + spyOn(websocketMsgSrvMock, 'renameNote') + scope.updateNoteName('') + expect(scope.note.name).toEqual(noteMock.name) + expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled() + scope.updateNoteName(' ') + expect(scope.note.name).toEqual(noteMock.name) + expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled() + scope.updateNoteName(scope.note.name) + expect(scope.note.name).toEqual(noteMock.name) + expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled() + }) - it('should update note name when updateNoteName() is called with a valid name', function() { - spyOn(websocketMsgSrvMock, 'renameNote'); - var newName = 'Your Note'; - scope.updateNoteName(newName); - expect(scope.note.name).toEqual(newName); - expect(websocketMsgSrvMock.renameNote).toHaveBeenCalled(); - }); + it('should update note name when updateNoteName() is called with a valid name', function () { + spyOn(websocketMsgSrvMock, 'renameNote') + let newName = 'Your Note' + scope.updateNoteName(newName) + expect(scope.note.name).toEqual(newName) + expect(websocketMsgSrvMock.renameNote).toHaveBeenCalled() + }) - it('should reload note info once per one "setNoteMenu" event', function() { - spyOn(websocketMsgSrvMock, 'getNote'); - spyOn(websocketMsgSrvMock, 'listRevisionHistory'); + it('should reload note info once per one "setNoteMenu" event', function () { + spyOn(websocketMsgSrvMock, 'getNote') + spyOn(websocketMsgSrvMock, 'listRevisionHistory') - scope.$broadcast('setNoteMenu'); - expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0); - expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0); + scope.$broadcast('setNoteMenu') + expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0) + expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0) - websocketMsgSrvMock.getNote.calls.reset(); - websocketMsgSrvMock.listRevisionHistory.calls.reset(); + websocketMsgSrvMock.getNote.calls.reset() + websocketMsgSrvMock.listRevisionHistory.calls.reset() - scope.$broadcast('setNoteMenu'); - expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0); - expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0); - }); -}); + scope.$broadcast('setNoteMenu') + expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0) + expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0) + }) +}) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html index 64da3cfff7..249e7c105b 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html @@ -20,7 +20,7 @@ limitations under the License.
+ ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'" + ng-enter="runParagraphFromButton(getEditorValue())" + ng-model="paragraph.settings.params[formulaire.name]" + ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }" + name="{{formulaire.name}}" + ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
+ paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
+ paragraph.settings.forms[formulaire.name].type == 'CheckBox'">