mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge remote-tracking branch 'origin/master' into livyInterperter
This commit is contained in:
commit
01ec47493b
10 changed files with 251 additions and 159 deletions
|
|
@ -105,6 +105,8 @@ public class Message {
|
|||
|
||||
ANGULAR_OBJECT_CLIENT_BIND, // [c-s] angular object updated from AngularJS z object
|
||||
|
||||
ANGULAR_OBJECT_CLIENT_UNBIND, // [c-s] angular object unbind from AngularJS z object
|
||||
|
||||
LIST_CONFIGURATIONS, // [c-s] ask all key/value pairs of configurations
|
||||
CONFIGURATIONS_INFO, // [s-c] all key/value pairs of configurations
|
||||
// @param settings serialized Map<String, String> object
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
case ANGULAR_OBJECT_CLIENT_BIND:
|
||||
angularObjectClientBind(conn, userAndRoles, notebook, messagereceived);
|
||||
break;
|
||||
case ANGULAR_OBJECT_CLIENT_UNBIND:
|
||||
angularObjectClientUnbind(conn, userAndRoles, notebook, messagereceived);
|
||||
break;
|
||||
case LIST_CONFIGURATIONS:
|
||||
sendAllConfigurations(conn, userAndRoles, notebook);
|
||||
break;
|
||||
|
|
@ -769,6 +772,45 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given Angular variable to the target
|
||||
* interpreter(s) angular registry given a noteId
|
||||
* and an optional list of paragraph id(s)
|
||||
* @param conn
|
||||
* @param notebook
|
||||
* @param fromMessage
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void angularObjectClientUnbind(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
Notebook notebook, Message fromMessage)
|
||||
throws Exception{
|
||||
String noteId = fromMessage.getType("noteId");
|
||||
String varName = fromMessage.getType("name");
|
||||
String paragraphId = fromMessage.getType("paragraphId");
|
||||
Note note = notebook.getNote(noteId);
|
||||
|
||||
if (paragraphId == null) {
|
||||
throw new IllegalArgumentException("target paragraph not specified for " +
|
||||
"angular value unBind");
|
||||
}
|
||||
|
||||
if (note != null) {
|
||||
final InterpreterGroup interpreterGroup = findInterpreterGroupForParagraph(note,
|
||||
paragraphId);
|
||||
|
||||
final AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
|
||||
|
||||
if (registry instanceof RemoteAngularObjectRegistry) {
|
||||
RemoteAngularObjectRegistry remoteRegistry = (RemoteAngularObjectRegistry) registry;
|
||||
removeAngularFromRemoteRegistry(noteId, paragraphId, varName, remoteRegistry,
|
||||
interpreterGroup.getId(), conn);
|
||||
} else {
|
||||
removeAngularObjectFromLocalRepo(noteId, paragraphId, varName, registry,
|
||||
interpreterGroup.getId(), conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InterpreterGroup findInterpreterGroupForParagraph(Note note, String paragraphId)
|
||||
throws Exception {
|
||||
final Paragraph paragraph = note.getParagraph(paragraphId);
|
||||
|
|
@ -794,6 +836,20 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
conn);
|
||||
}
|
||||
|
||||
private void removeAngularFromRemoteRegistry(String noteId, String paragraphId,
|
||||
String varName, RemoteAngularObjectRegistry remoteRegistry,
|
||||
String interpreterGroupId, NotebookSocket conn) {
|
||||
final AngularObject ao = remoteRegistry.removeAndNotifyRemoteProcess(varName, noteId,
|
||||
paragraphId);
|
||||
this.broadcastExcept(
|
||||
noteId,
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", ao)
|
||||
.put("interpreterGroupId", interpreterGroupId)
|
||||
.put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId),
|
||||
conn);
|
||||
}
|
||||
|
||||
private void pushAngularObjectToLocalRepo(String noteId, String paragraphId, String varName,
|
||||
Object varValue, AngularObjectRegistry registry,
|
||||
String interpreterGroupId, NotebookSocket conn) {
|
||||
|
|
@ -813,6 +869,20 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
conn);
|
||||
}
|
||||
|
||||
private void removeAngularObjectFromLocalRepo(String noteId, String paragraphId, String varName,
|
||||
AngularObjectRegistry registry, String interpreterGroupId, NotebookSocket conn) {
|
||||
final AngularObject removed = registry.remove(varName, noteId, paragraphId);
|
||||
if (removed != null) {
|
||||
this.broadcastExcept(
|
||||
noteId,
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", removed)
|
||||
.put("interpreterGroupId", interpreterGroupId)
|
||||
.put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId),
|
||||
conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
|
||||
Message fromMessage) throws IOException {
|
||||
final String paragraphId = (String) fromMessage.get("id");
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ abstract public class AbstractZeppelinIT {
|
|||
protected WebDriver driver;
|
||||
|
||||
protected final static Logger LOG = LoggerFactory.getLogger(AbstractZeppelinIT.class);
|
||||
protected static final long MAX_IMPLICIT_WAIT = 30;
|
||||
protected static final long MAX_BROWSER_TIMEOUT_SEC = 30;
|
||||
protected static final long MAX_PARAGRAPH_TIMEOUT_SEC = 60;
|
||||
|
||||
|
|
@ -59,6 +60,19 @@ abstract public class AbstractZeppelinIT {
|
|||
}
|
||||
}
|
||||
|
||||
protected void setTextOfParagraph(int paragraphNo, String text) {
|
||||
String editorId = driver.findElement(By.xpath(getParagraphXPath(paragraphNo) + "//div[contains(@class, 'editor')]")).getAttribute("id");
|
||||
if (driver instanceof JavascriptExecutor) {
|
||||
((JavascriptExecutor) driver).executeScript("ace.edit('" + editorId + "'). setValue('" + text + "')");
|
||||
} else {
|
||||
throw new IllegalStateException("This driver does not support JavaScript!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void runParagraph(int paragraphNo) {
|
||||
driver.findElement(By.xpath(getParagraphXPath(paragraphNo) + "//span[@class='icon-control-play']")).click();
|
||||
}
|
||||
|
||||
|
||||
protected String getParagraphXPath(int paragraphNo) {
|
||||
return "//div[@ng-controller=\"ParagraphCtrl\"][" + paragraphNo + "]";
|
||||
|
|
@ -135,40 +149,6 @@ abstract public class AbstractZeppelinIT {
|
|||
sleep(100, true);
|
||||
}
|
||||
|
||||
public enum HelperKeys implements CharSequence {
|
||||
OPEN_PARENTHESIS(Keys.chord(Keys.SHIFT, "9")),
|
||||
EXCLAMATION(Keys.chord(Keys.SHIFT, "1")),
|
||||
PERCENTAGE(Keys.chord(Keys.SHIFT, "5")),
|
||||
SHIFT_ENTER(Keys.chord(SHIFT, ENTER));
|
||||
|
||||
private final CharSequence keyCode;
|
||||
|
||||
HelperKeys(CharSequence keyCode) {
|
||||
this.keyCode = keyCode;
|
||||
}
|
||||
|
||||
public char charAt(int index) {
|
||||
return index == 0 ? keyCode.charAt(index) : '\ue000';
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
if (start == 0 && end == 1) {
|
||||
return String.valueOf(this.keyCode);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.valueOf(this.keyCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void handleException(String message, Exception e) throws Exception {
|
||||
LOG.error(message, e);
|
||||
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
|
@ -114,6 +115,8 @@ public class WebDriverManager {
|
|||
|
||||
long start = System.currentTimeMillis();
|
||||
boolean loaded = false;
|
||||
driver.manage().timeouts().implicitlyWait(AbstractZeppelinIT.MAX_IMPLICIT_WAIT,
|
||||
TimeUnit.SECONDS);
|
||||
driver.get(url);
|
||||
|
||||
while (System.currentTimeMillis() - start < 60 * 1000) {
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();
|
||||
ZeppelinITUtils.sleep(1000, false);
|
||||
|
||||
WebElement oldParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
oldParagraphEditor.sendKeys(" original paragraph ");
|
||||
setTextOfParagraph(1, " original paragraph ");
|
||||
|
||||
WebElement newPara = driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class,'new-paragraph')][1]"));
|
||||
action.moveToElement(newPara).click().build().perform();
|
||||
|
|
@ -98,8 +97,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
collector.checkThat("Paragraph is created above",
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'editor')]")).getText(),
|
||||
CoreMatchers.equalTo(""));
|
||||
WebElement aboveParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
aboveParagraphEditor.sendKeys(" this is above ");
|
||||
setTextOfParagraph(1, " this is above ");
|
||||
|
||||
newPara = driver.findElement(By.xpath(getParagraphXPath(2) + "//div[contains(@class,'new-paragraph')][2]"));
|
||||
action.moveToElement(newPara).click().build().perform();
|
||||
|
|
@ -109,8 +107,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
collector.checkThat("Paragraph is created below",
|
||||
driver.findElement(By.xpath(getParagraphXPath(3) + "//div[contains(@class, 'editor')]")).getText(),
|
||||
CoreMatchers.equalTo(""));
|
||||
WebElement belowParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(3) + "//textarea"));
|
||||
belowParagraphEditor.sendKeys(" this is below ");
|
||||
setTextOfParagraph(3, " this is below ");
|
||||
|
||||
collector.checkThat("The output field of paragraph1 contains",
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'editor')]")).getText(),
|
||||
|
|
@ -163,7 +160,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
deleteTestNotebook(driver);
|
||||
|
||||
} catch (Exception e) {
|
||||
handleException("Exception in ParagraphActionsIT while testMoveUpAndDown ", e);
|
||||
handleException("Exception in ParagraphActionsIT while testRemoveButton ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,16 +173,14 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
createNewNote();
|
||||
|
||||
waitForParagraph(1, "READY");
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
paragraph1Editor.sendKeys("1");
|
||||
setTextOfParagraph(1, "1");
|
||||
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//ul/li/a[@ng-click='insertNew()']")).click();
|
||||
|
||||
|
||||
waitForParagraph(2, "READY");
|
||||
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
|
||||
paragraph2Editor.sendKeys("2");
|
||||
setTextOfParagraph(2, "2");
|
||||
|
||||
|
||||
collector.checkThat("The paragraph1 value contains",
|
||||
|
|
@ -236,9 +231,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
createNewNote();
|
||||
|
||||
waitForParagraph(1, "READY");
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
|
||||
+ "abcd\")");
|
||||
setTextOfParagraph(1, "println (\"abcd\")");
|
||||
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
|
||||
driver.findElement(By.xpath(getParagraphXPath(1) + "//ul/li/a[@ng-click='toggleEnableDisable()']")).click();
|
||||
|
|
@ -275,9 +268,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
|
||||
waitForParagraph(1, "READY");
|
||||
String xpathToOutputField=getParagraphXPath(1) + "//div[contains(@ng-if,'getResultType()')]";
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
|
||||
+ "abcd\")");
|
||||
setTextOfParagraph(1, "println (\"abcd\")");
|
||||
collector.checkThat("Before Run Output field contains ",
|
||||
driver.findElement(By.xpath(xpathToOutputField)).getText(),
|
||||
CoreMatchers.equalTo(""));
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ import org.openqa.selenium.WebElement;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.apache.zeppelin.AbstractZeppelinIT.HelperKeys.*;
|
||||
import static org.openqa.selenium.Keys.*;
|
||||
|
||||
public class SparkParagraphIT extends AbstractZeppelinIT {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SparkParagraphIT.class);
|
||||
|
||||
|
|
@ -67,17 +64,10 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
paragraph1Editor.sendKeys("sc.version");
|
||||
paragraph1Editor.sendKeys(SHIFT_ENTER);
|
||||
setTextOfParagraph(1, "sc.version");
|
||||
runParagraph(1);
|
||||
|
||||
waitForParagraph(1, "FINISHED");
|
||||
WebElement paragraph1Result = driver.findElement(By.xpath(
|
||||
getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
|
||||
Float sparkVersion = Float.parseFloat(paragraph1Result.getText().split("= ")[1].substring(0, 3));
|
||||
|
||||
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
|
||||
|
||||
|
||||
/*
|
||||
equivalent of
|
||||
|
|
@ -90,50 +80,15 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
|
|||
val bank = bankText.map(s => s.split(";")).filter(s => s(0) != "\"age\"").map(s => Bank(s(0).toInt,s(1).replaceAll("\"", ""),s(2).replaceAll("\"", ""),s(3).replaceAll("\"", ""),s(5).replaceAll("\"", "").toInt)).toDF()
|
||||
bank.registerTempTable("bank")
|
||||
*/
|
||||
paragraph2Editor.sendKeys("import org.apache.commons.io.IOUtils" +
|
||||
ENTER +
|
||||
|
||||
"import java.net.URL" +
|
||||
ENTER +
|
||||
|
||||
"import java.nio.charset.Charset" +
|
||||
ENTER +
|
||||
|
||||
"val bankText = sc.parallelize" + OPEN_PARENTHESIS +
|
||||
"IOUtils.toString" + OPEN_PARENTHESIS + "new URL" + OPEN_PARENTHESIS
|
||||
+ "\"https://s3.amazonaws.com/apache" + SUBTRACT + "zeppelin/tutorial/bank/bank." +
|
||||
"csv\"),Charset.forName" + OPEN_PARENTHESIS + "\"utf8\"))" +
|
||||
".split" + OPEN_PARENTHESIS + "\"\\n\"))" +
|
||||
ENTER +
|
||||
|
||||
"case class Bank" + OPEN_PARENTHESIS +
|
||||
"age: Integer, job: String, marital: String, education: String, balance: Integer)" +
|
||||
ENTER +
|
||||
ENTER +
|
||||
|
||||
"val bank = bankText.map" + OPEN_PARENTHESIS + "s => s.split" +
|
||||
OPEN_PARENTHESIS + "\";\")).filter" + OPEN_PARENTHESIS +
|
||||
"s => s" + OPEN_PARENTHESIS + "0) " + EXCLAMATION +
|
||||
"= \"\\\"age\\\"\").map" + OPEN_PARENTHESIS +
|
||||
"s => Bank" + OPEN_PARENTHESIS + "s" + OPEN_PARENTHESIS +
|
||||
"0).toInt,s" + OPEN_PARENTHESIS + "1).replaceAll" +
|
||||
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
|
||||
"s" + OPEN_PARENTHESIS + "2).replaceAll" +
|
||||
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
|
||||
"s" + OPEN_PARENTHESIS + "3).replaceAll" +
|
||||
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
|
||||
"s" + OPEN_PARENTHESIS + "5).replaceAll" +
|
||||
OPEN_PARENTHESIS + "\"\\\"\", \"\").toInt" + ")" +
|
||||
")" + (sparkVersion < 1.3f ? "" : ".toDF" + OPEN_PARENTHESIS + ")") +
|
||||
ENTER +
|
||||
|
||||
"bank.registerTempTable" + OPEN_PARENTHESIS + "\"bank\")"
|
||||
);
|
||||
paragraph2Editor.sendKeys("" + END + BACK_SPACE + BACK_SPACE +
|
||||
BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE +
|
||||
BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE);
|
||||
|
||||
paragraph2Editor.sendKeys(SHIFT_ENTER);
|
||||
setTextOfParagraph(2, "import org.apache.commons.io.IOUtils\\n" +
|
||||
"import java.net.URL\\n" +
|
||||
"import java.nio.charset.Charset\\n" +
|
||||
"val bankText = sc.parallelize(IOUtils.toString(new URL(\"https://s3.amazonaws.com/apache-zeppelin/tutorial/bank/bank.csv\"),Charset.forName(\"utf8\")).split(\"\\\\n\"))\\n" +
|
||||
"case class Bank(age: Integer, job: String, marital: String, education: String, balance: Integer)\\n" +
|
||||
"\\n" +
|
||||
"val bank = bankText.map(s => s.split(\";\")).filter(s => s(0) != \"\\\\\"age\\\\\"\").map(s => Bank(s(0).toInt,s(1).replaceAll(\"\\\\\"\", \"\"),s(2).replaceAll(\"\\\\\"\", \"\"),s(3).replaceAll(\"\\\\\"\", \"\"),s(5).replaceAll(\"\\\\\"\", \"\").toInt)).toDF()\\n" +
|
||||
"bank.registerTempTable(\"bank\")");
|
||||
runParagraph(2);
|
||||
|
||||
try {
|
||||
waitForParagraph(2, "FINISHED");
|
||||
|
|
@ -164,14 +119,11 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
setTextOfParagraph(1, "%pyspark\\n" +
|
||||
"for x in range(0, 3):\\n" +
|
||||
" print \"test loop %d\" % (x)");
|
||||
|
||||
paragraph1Editor.sendKeys(PERCENTAGE + "pyspark" + ENTER +
|
||||
"for x in range" + OPEN_PARENTHESIS + "0, 3):" + ENTER +
|
||||
" print \"test loop " + PERCENTAGE + "d\" " +
|
||||
PERCENTAGE + " " + OPEN_PARENTHESIS + "x)" + ENTER);
|
||||
|
||||
paragraph1Editor.sendKeys(SHIFT_ENTER);
|
||||
runParagraph(1);
|
||||
|
||||
try {
|
||||
waitForParagraph(1, "FINISHED");
|
||||
|
|
@ -199,12 +151,9 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
|
||||
paragraph1Editor.sendKeys(PERCENTAGE + "sql" + ENTER +
|
||||
setTextOfParagraph(1,"%sql\\n" +
|
||||
"select * from bank limit 1");
|
||||
|
||||
paragraph1Editor.sendKeys(SHIFT_ENTER);
|
||||
runParagraph(1);
|
||||
|
||||
try {
|
||||
waitForParagraph(1, "FINISHED");
|
||||
|
|
|
|||
|
|
@ -17,21 +17,21 @@
|
|||
|
||||
package org.apache.zeppelin.integration;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.zeppelin.AbstractZeppelinIT;
|
||||
import org.apache.zeppelin.WebDriverManager;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.openqa.selenium.*;
|
||||
import org.junit.rules.ErrorCollector;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test Zeppelin with web browser.
|
||||
|
|
@ -47,6 +47,8 @@ import static org.junit.Assert.assertTrue;
|
|||
public class ZeppelinIT extends AbstractZeppelinIT {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZeppelinIT.class);
|
||||
|
||||
@Rule
|
||||
public ErrorCollector collector = new ErrorCollector();
|
||||
|
||||
@Before
|
||||
public void startUp() {
|
||||
|
|
@ -80,13 +82,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* print angular template
|
||||
* %angular <div id='angularTestButton' ng-click='myVar=myVar+1'>BindingTest_{{myVar}}_</div>
|
||||
*/
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
|
||||
+ Keys.chord(Keys.SHIFT, "5")
|
||||
+ "angular <div id='angularTestButton' "
|
||||
+ "ng" + Keys.chord(Keys.SUBTRACT) + "click='myVar=myVar+1'>"
|
||||
+ "BindingTest_{{myVar}}_</div>\")");
|
||||
paragraph1Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(1, "println(\"%angular <div id=\\'angularTestButton\\' ng-click=\\'myVar=myVar+1\\'>BindingTest_{{myVar}}_</div>\")");
|
||||
runParagraph(1);
|
||||
waitForParagraph(1, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
|
|
@ -98,9 +95,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* z.angularBind("myVar", 1)
|
||||
*/
|
||||
assertEquals(1, driver.findElements(By.xpath(getParagraphXPath(2) + "//textarea")).size());
|
||||
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
|
||||
paragraph2Editor.sendKeys("z.angularBind" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\", 1)");
|
||||
paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(2, "z.angularBind(\"myVar\", 1)");
|
||||
runParagraph(2);
|
||||
waitForParagraph(2, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
|
|
@ -112,11 +108,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* print variable
|
||||
* print("myVar="+z.angular("myVar"))
|
||||
*/
|
||||
WebElement paragraph3Editor = driver.findElement(By.xpath(getParagraphXPath(3) + "//textarea"));
|
||||
paragraph3Editor.sendKeys(
|
||||
"print" + Keys.chord(Keys.SHIFT, "9") + "\"myVar=\"" + Keys.chord(Keys.ADD)
|
||||
+ "z.angular" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\"))");
|
||||
paragraph3Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(3, "print(\"myVar=\"+z.angular(\"myVar\"))");
|
||||
runParagraph(3);
|
||||
waitForParagraph(3, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
|
|
@ -139,13 +132,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* z.run(2, context)
|
||||
* }
|
||||
*/
|
||||
WebElement paragraph4Editor = driver.findElement(By.xpath(getParagraphXPath(4) + "//textarea"));
|
||||
paragraph4Editor.sendKeys(
|
||||
"z.angularWatch" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\", "
|
||||
+ Keys.chord(Keys.SHIFT, "9")
|
||||
+ "before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)"
|
||||
+ Keys.EQUALS + ">{ z.run" +Keys.chord(Keys.SHIFT, "9") + "2, context)}");
|
||||
paragraph4Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2, context)})");
|
||||
runParagraph(4);
|
||||
waitForParagraph(4, "FINISHED");
|
||||
|
||||
|
||||
|
|
@ -168,10 +156,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* Unbind
|
||||
* z.angularUnbind("myVar")
|
||||
*/
|
||||
WebElement paragraph5Editor = driver.findElement(By.xpath(getParagraphXPath(5) + "//textarea"));
|
||||
paragraph5Editor.sendKeys(
|
||||
"z.angularUnbind" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\")");
|
||||
paragraph5Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(5, "z.angularUnbind(\"myVar\")");
|
||||
runParagraph(5);
|
||||
waitForParagraph(5, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
|
|
@ -181,8 +167,7 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
/*
|
||||
* Bind again and see rebind works.
|
||||
*/
|
||||
paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
|
||||
paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
runParagraph(2);
|
||||
waitForParagraph(2, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
|
|
@ -228,15 +213,19 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
// wait for first paragraph's " READY " status text
|
||||
waitForParagraph(1, "READY");
|
||||
|
||||
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
|
||||
|
||||
paragraph1Editor.sendKeys("import org.apache.commons.csv.CSVFormat");
|
||||
paragraph1Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
|
||||
setTextOfParagraph(1, "import org.apache.commons.csv.CSVFormat");
|
||||
runParagraph(1);
|
||||
waitForParagraph(1, "FINISHED");
|
||||
|
||||
// check expected text
|
||||
assertTrue(waitForText("import org.apache.commons.csv.CSVFormat",
|
||||
By.xpath(getParagraphXPath(1) + "//div[starts-with(@id, 'p') and contains(@id, 'text')]/div")));
|
||||
WebElement paragraph1Result = driver.findElement(By.xpath(
|
||||
getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
|
||||
|
||||
collector.checkThat("Paragraph from ZeppelinIT of testSparkInterpreterDependencyLoading result: ",
|
||||
paragraph1Result.getText().toString(), CoreMatchers.containsString(
|
||||
"import org.apache.commons.csv.CSVFormat"
|
||||
)
|
||||
);
|
||||
|
||||
//delete created notebook for cleanup.
|
||||
deleteTestNotebook(driver);
|
||||
|
|
|
|||
|
|
@ -262,6 +262,96 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
verify(otherConn).send(mdMsg1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_unbind_angular_object_from_remote_for_paragraphs() throws Exception {
|
||||
//Given
|
||||
final String varName = "name";
|
||||
final String value = "val";
|
||||
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_UNBIND)
|
||||
.put("noteId", "noteId")
|
||||
.put("name", varName)
|
||||
.put("paragraphId", "paragraphId");
|
||||
|
||||
final NotebookServer server = new NotebookServer();
|
||||
final Notebook notebook = mock(Notebook.class);
|
||||
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
|
||||
when(notebook.getNote("noteId")).thenReturn(note);
|
||||
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
|
||||
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
|
||||
|
||||
final RemoteAngularObjectRegistry mdRegistry = mock(RemoteAngularObjectRegistry.class);
|
||||
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
|
||||
mdGroup.setAngularObjectRegistry(mdRegistry);
|
||||
|
||||
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
|
||||
|
||||
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
|
||||
when(mdRegistry.removeAndNotifyRemoteProcess(varName, "noteId", "paragraphId")).thenReturn(ao1);
|
||||
NotebookSocket conn = mock(NotebookSocket.class);
|
||||
NotebookSocket otherConn = mock(NotebookSocket.class);
|
||||
|
||||
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_REMOVE)
|
||||
.put("angularObject", ao1)
|
||||
.put("interpreterGroupId", "mdGroup")
|
||||
.put("noteId", "noteId")
|
||||
.put("paragraphId", "paragraphId"));
|
||||
|
||||
server.noteSocketMap.put("noteId", asList(conn, otherConn));
|
||||
|
||||
// When
|
||||
server.angularObjectClientUnbind(conn, new HashSet<String>(), notebook, messageReceived);
|
||||
|
||||
// Then
|
||||
verify(mdRegistry, never()).removeAndNotifyRemoteProcess(varName, "noteId", null);
|
||||
|
||||
verify(otherConn).send(mdMsg1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_unbind_angular_object_from_local_for_paragraphs() throws Exception {
|
||||
//Given
|
||||
final String varName = "name";
|
||||
final String value = "val";
|
||||
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_UNBIND)
|
||||
.put("noteId", "noteId")
|
||||
.put("name", varName)
|
||||
.put("paragraphId", "paragraphId");
|
||||
|
||||
final NotebookServer server = new NotebookServer();
|
||||
final Notebook notebook = mock(Notebook.class);
|
||||
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
|
||||
when(notebook.getNote("noteId")).thenReturn(note);
|
||||
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
|
||||
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
|
||||
|
||||
final AngularObjectRegistry mdRegistry = mock(AngularObjectRegistry.class);
|
||||
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
|
||||
mdGroup.setAngularObjectRegistry(mdRegistry);
|
||||
|
||||
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
|
||||
|
||||
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
|
||||
|
||||
|
||||
when(mdRegistry.remove(varName, "noteId", "paragraphId")).thenReturn(ao1);
|
||||
|
||||
NotebookSocket conn = mock(NotebookSocket.class);
|
||||
NotebookSocket otherConn = mock(NotebookSocket.class);
|
||||
|
||||
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_REMOVE)
|
||||
.put("angularObject", ao1)
|
||||
.put("interpreterGroupId", "mdGroup")
|
||||
.put("noteId", "noteId")
|
||||
.put("paragraphId", "paragraphId"));
|
||||
server.noteSocketMap.put("noteId", asList(conn, otherConn));
|
||||
|
||||
// When
|
||||
server.angularObjectClientUnbind(conn, new HashSet<String>(), notebook, messageReceived);
|
||||
|
||||
// Then
|
||||
verify(otherConn).send(mdMsg1);
|
||||
}
|
||||
|
||||
private NotebookSocket createWebSocket() {
|
||||
NotebookSocket sock = mock(NotebookSocket.class);
|
||||
when(sock.getRequest()).thenReturn(createHttpServletRequest());
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ angular.module('zeppelinWebApp')
|
|||
if (paragraphId) {
|
||||
websocketMsgSrv.clientBindAngularObject($routeParams.noteId, varName, value, paragraphId);
|
||||
}
|
||||
},
|
||||
// Example: z.angularUnBind('my_var', '20150213-231621_168813393')
|
||||
angularUnbind: function(varName, paragraphId) {
|
||||
// Only push to server if paragraphId is defined
|
||||
if (paragraphId) {
|
||||
websocketMsgSrv.clientUnbindAngularObject($routeParams.noteId, varName, paragraphId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,17 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
|
|||
});
|
||||
},
|
||||
|
||||
clientUnbindAngularObject: function(noteId, name, paragraphId) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'ANGULAR_OBJECT_CLIENT_UNBIND',
|
||||
data: {
|
||||
noteId: noteId,
|
||||
name: name,
|
||||
paragraphId: paragraphId
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancelParagraphRun: function(paragraphId) {
|
||||
websocketEvents.sendNewEvent({op: 'CANCEL_PARAGRAPH', data: {id: paragraphId}});
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue