You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2016/04/12 06:19:19 UTC

incubator-zeppelin git commit: [ZEPPELIN-697] Replace dynamic form with angular object from registry if exists

Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master f43228bd1 -> 758abc688


[ZEPPELIN-697] Replace dynamic form with angular object from registry if exists

### What is this PR for?
Replace dynamic form with angular object from registry if exists

I updated the `Paragraph.jobRun()` method to look for existing variable from the Angular Object Registry first before displaying the dynamic form.

We look for Angular object having same name:

* first at paragraph scope (note id + paragraph id)
* then at note scope (note id only)

I did not look at **global** scope because Leemoonsoo  was mentioning somewhere that we may likely remove the global scope some day

_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-697]**

### How should this be tested?
* `git fetch origin pull/745/head:AngularObjectReplaceDynamicFormVar`
* `git checkout AngularObjectReplaceDynamicFormVar`
* `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'); z.runParagraph('PUT_HERE_PARAGRAPH_ID')"> Bind</button>
    <button type="submit" class="btn btn-primary" ng-click="z.angularUnbind('superhero','PUT_HERE_PARAGRAPH_ID'); z.runParagraph('PUT_HERE_PARAGRAPH_ID')"> Unbind</button>
</form>
</form>
```
* Create a second paragraph with the following code:
```scala
%md

### The superhero is : **${superhero}**
```
* In the first paragraph, replace the text PUT_HERE_PARAGRAPH_ID by the paragraph id of the second paragraph
* Execute first the second paragraph to see that the dynamic form system is working as usual
* Now put **Batman** in the input text of the first paragraph and click alternatively on **Bind** to see that dynamic form in the second paragraph is removed
* Click on **Unbind** to see that the dynamic form system is back

### Screenshots (if appropriate)
![replacedynamicform](https://cloud.githubusercontent.com/assets/1532977/14233999/34ea658a-f9d8-11e5-875d-da04dd5d0a9b.gif)

### Questions:
* Does the licenses files need update? --> **No**
* Is there breaking changes for older versions? --> **Yes**
* Does this needs documentation? --> **Yes**

[ZEPPELIN-635]: https://issues.apache.org/jira/browse/ZEPPELIN-635
[ZEPPELIN-697]: https://issues.apache.org/jira/browse/ZEPPELIN-697

Author: DuyHai DOAN <do...@gmail.com>

Closes #745 from doanduyhai/ZEPPELIN-697 and squashes the following commits:

be4dbd1 [DuyHai DOAN] [ZEPPELIN-697] Replace dynamic form with angular object from registry if exists


Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/758abc68
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/758abc68
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/758abc68

Branch: refs/heads/master
Commit: 758abc6886e1d1e062341cb23b5ac04b861c6578
Parents: f43228b
Author: DuyHai DOAN <do...@gmail.com>
Authored: Sun Apr 3 20:02:09 2016 +0200
Committer: Lee moon soo <mo...@apache.org>
Committed: Tue Apr 12 13:19:09 2016 +0900

----------------------------------------------------------------------
 .../org/apache/zeppelin/notebook/Paragraph.java | 28 +++++++++++
 .../zeppelin/display/AngularObjectBuilder.java  | 26 ++++++++++
 .../apache/zeppelin/notebook/ParagraphTest.java | 52 +++++++++++++++++++-
 3 files changed, 105 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/758abc68/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index bb4d69b..5778d0d 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -17,6 +17,7 @@
 
 package org.apache.zeppelin.notebook;
 
+import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.apache.zeppelin.display.GUI;
@@ -232,6 +233,12 @@ public class Paragraph extends Job implements Serializable, Cloneable {
       String scriptBody = getScriptBody();
       Map<String, Input> inputs = Input.extractSimpleQueryParam(scriptBody); // inputs will be built
                                                                              // from script body
+
+      final AngularObjectRegistry angularRegistry = repl.getInterpreterGroup()
+              .getAngularObjectRegistry();
+
+      scriptBody = extractVariablesFromAngularRegistry(scriptBody, inputs, angularRegistry);
+
       settings.setForms(inputs);
       script = Input.getSimpleQuery(settings.getParams(), scriptBody);
     }
@@ -390,4 +397,25 @@ public class Paragraph extends Job implements Serializable, Cloneable {
     Paragraph paraClone = (Paragraph) this.clone();
     return paraClone;
   }
+
+  String extractVariablesFromAngularRegistry(String scriptBody, Map<String, Input> inputs,
+                                             AngularObjectRegistry angularRegistry) {
+
+    final String noteId = this.getNote().getId();
+    final String paragraphId = this.getId();
+
+    final Set<String> keys = new HashSet<>(inputs.keySet());
+
+    for (String varName : keys) {
+      final AngularObject paragraphScoped = angularRegistry.get(varName, noteId, paragraphId);
+      final AngularObject noteScoped = angularRegistry.get(varName, noteId, null);
+      final AngularObject angularObject = paragraphScoped != null ? paragraphScoped : noteScoped;
+      if (angularObject != null) {
+        inputs.remove(varName);
+        final String pattern = "[$][{]\\s*" + varName + "\\s*(?:=[^}]+)?[}]";
+        scriptBody = scriptBody.replaceAll(pattern, angularObject.get().toString());
+      }
+    }
+    return scriptBody;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/758abc68/zeppelin-zengine/src/test/java/org/apache/zeppelin/display/AngularObjectBuilder.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/display/AngularObjectBuilder.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/display/AngularObjectBuilder.java
new file mode 100644
index 0000000..c201858
--- /dev/null
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/display/AngularObjectBuilder.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+public class AngularObjectBuilder {
+
+    public static <T> AngularObject<T> build(String varName, T value, String noteId,
+                                             String paragraphId) {
+        return new AngularObject<>(varName, value, noteId, paragraphId, null);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/758abc68/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java
index 87805ce..a594873 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java
@@ -17,9 +17,20 @@
 
 package org.apache.zeppelin.notebook;
 
-import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.zeppelin.display.AngularObject;
+import org.apache.zeppelin.display.AngularObjectBuilder;
+import org.apache.zeppelin.display.AngularObjectRegistry;
+import org.apache.zeppelin.display.Input;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
 
 public class ParagraphTest {
   @Test
@@ -35,4 +46,43 @@ public class ParagraphTest {
     String text = "12345678";
     assertEquals(text, Paragraph.getScriptBody(text));
   }
+
+  @Test
+  public void should_extract_variable_from_angular_object_registry() throws Exception {
+    //Given
+    final String noteId = "noteId";
+
+    final AngularObjectRegistry registry = mock(AngularObjectRegistry.class);
+    final Note note = mock(Note.class);
+    final Map<String, Input> inputs = new HashMap<>();
+    inputs.put("name", null);
+    inputs.put("age", null);
+    inputs.put("job", null);
+
+    final String scriptBody = "My name is ${name} and I am ${age=20} years old. " +
+            "My occupation is ${ job = engineer | developer | artists}";
+
+    final Paragraph paragraph = new Paragraph(note, null, null);
+    final String paragraphId = paragraph.getId();
+
+    final AngularObject nameAO = AngularObjectBuilder.build("name", "DuyHai DOAN", noteId,
+            paragraphId);
+
+    final AngularObject ageAO = AngularObjectBuilder.build("age", 34, noteId, null);
+
+    when(note.getId()).thenReturn(noteId);
+    when(registry.get("name", noteId, paragraphId)).thenReturn(nameAO);
+    when(registry.get("age", noteId, null)).thenReturn(ageAO);
+
+    final String expected = "My name is DuyHai DOAN and I am 34 years old. " +
+            "My occupation is ${ job = engineer | developer | artists}";
+    //When
+    final String actual = paragraph.extractVariablesFromAngularRegistry(scriptBody, inputs,
+            registry);
+
+    //Then
+    verify(registry).get("name", noteId, paragraphId);
+    verify(registry).get("age", noteId, null);
+    assertEquals(actual, expected);
+  }
 }