Repository: incubator-zeppelin
Updated Branches:
refs/heads/master 0629d9370 -> 34a52edd6
[ZEPPELIN-693] Add AngularJS z.angularUnbind()
### What is this PR for?
Add AngularJS `z.angularUnbind()` method
The signature of the method is `angularUnbind(varName, paragraphId)`.
_This is a sub-task of epic **[ZEPPELIN-635]**_
### What type of PR is it?
[Improvement]
### Todos
* [ ] - Code review
* [ ] - Simple test
### Is there a relevant Jira issue?
**[ZEPPELIN-693]**
### How should this be tested?
* `git fetch origin pull/741/head:AngularJSUnbind`
* `git checkout AngularJSUnbind`
* `mvn clean package -DskipTests`
* `bin/zeppelin-daemon.sh restart`
* Create a new note
* In the first paragraph, put the following code
```html
%angular
<form class="form-inline">
<div class="form-group">
<label for="superheroId">Super Hero: </label>
<input type="text" class="form-control" id="superheroId"
placeholder="Superhero name ..." ng-model="superhero"></input>
</div>
<button type="submit" class="btn btn-primary"
ng-click="z.angularBind('superhero', superhero, PUT_HERE_PARAGRAPH_ID')">
Angular Bind</button>
<button type="submit" class="btn btn-primary"
ng-click="z.angularUnbind('superhero', 'PUT_HERE_PARAGRAPH_ID')"> Angular
UnBind</button>
</form>
```
* Create a second paragraph with the following code:
```scala
z.angular("superhero")
```
* Retrieve the paragraph id of the second paragraph
* In the first paragraph, replace the text PUT_HERE_PARAGRAPH_ID by the correct
paragraph id
* In the input text, put "Superman" and click on the **Bind Angular** button
* Execute the second paragraph to see that the superhero variable is now set to
Superman
* Now click on the **Unbind Angular** button from the first paragraph
* Refresh the second paragraph to check that the _superhero_ variable has been
removed
### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? --> **No**
* Is there breaking changes for older versions? --> **No**
* Does this needs documentation? --> **Yes**
[ZEPPELIN-635]: https://issues.apache.org/jira/browse/ZEPPELIN-635
[ZEPPELIN-693]: https://issues.apache.org/jira/browse/ZEPPELIN-693
Author: DuyHai DOAN <[email protected]>
Closes #741 from doanduyhai/ZEPPELIN-693 and squashes the following commits:
3f01b04 [DuyHai DOAN] [ZEPPELIN-693] Add AngularJS z.angularUnbind()
Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo
Commit:
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/34a52edd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/34a52edd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/34a52edd
Branch: refs/heads/master
Commit: 34a52edd6c4ec95869f9e37af9f92f11e1ab2b99
Parents: 0629d93
Author: DuyHai DOAN <[email protected]>
Authored: Thu Mar 17 15:50:29 2016 +0100
Committer: Lee moon soo <[email protected]>
Committed: Tue Mar 22 10:02:12 2016 -0700
----------------------------------------------------------------------
.../org/apache/zeppelin/socket/Message.java | 2 +
.../apache/zeppelin/socket/NotebookServer.java | 70 +++++++++++++++
.../zeppelin/socket/NotebookServerTest.java | 90 ++++++++++++++++++++
.../notebook/paragraph/paragraph.controller.js | 7 ++
.../websocketEvents/websocketMsg.service.js | 11 +++
5 files changed, 180 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/34a52edd/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
----------------------------------------------------------------------
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
index f091364..913e184 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java
@@ -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
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/34a52edd/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
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 98a1aaa..9412d71 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
@@ -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");
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/34a52edd/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java
----------------------------------------------------------------------
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 6989c16..98895de 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
@@ -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());
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/34a52edd/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index 097ee84..a6489fd 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -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);
+ }
}
};
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/34a52edd/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
----------------------------------------------------------------------
diff --git
a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
index 3fba0f5..3b4df03 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
@@ -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}});
},