Repository: zeppelin
Updated Branches:
  refs/heads/master 1f7366479 -> 89a1c53f2


[ZEPPELIN-2106] providing paragraph config in create note/paragraph rest call

### What is this PR for?
* Allow to provide full paragraph config directly in the Create Paragraph and 
Create Note endpoint.
* This saves some calls to [noteId]/paragraph/[paragraphId]/config
* Updated doc.

### What type of PR is it?
Improvement

### Todos

### What is the Jira issue?
[ZEPPELIN-2106](https://issues.apache.org/jira/browse/ZEPPELIN-2106)

### How should this be tested?
Outline the steps to test the PR here.

1. Clone the first paragraph of 'Zeppelin Tutorial/Basic Features (Spark)' to 
get the bank data loaded in a new note.
2.  curl -X POST -d testAPI.json 
http://localhost:8080/api/notebook/$YOURNOTEID/paragraph
3. When running the paragraphes, the graphs will show up with the appropriate 
settings.

testAPI.json:
`{
        "title":"Example providing config",
        "text":"%sql\nselect age, marital, count(1) cvalue from bank group by 
age, marital order by age",
        "config": {
          "title":true,
          "colWidth":6.0,
          "results": [
                  {
                          "graph": {
                                  "mode": "scatterChart",
                                  "optionOpen": true
                          }
                  }
          ]
        },
        "colWidth":9.0
}
`
### Screenshots (if appropriate)

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

Author: Remilito <remy.ga...@gmail.com>

Closes #2099 from Remilito/ZEPPELIN-2106b and squashes the following commits:

0994ac0 [Remilito] [ZEPPELIN-2106]: keeping only the API providing the whole 
config
76af44a [Remilito] [ZEPPELIN-2106] providing paragraph config in create 
note/paragraph call * Allow to describe graph, colWidth, showTitle or full 
paragraph config directly in the Create Paragraph and Create Note endpoint. * 
Updated doc.


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

Branch: refs/heads/master
Commit: 89a1c53f247688c1f521f8c2a8622a8484cb23cf
Parents: 1f73664
Author: Remilito <remy.ga...@gmail.com>
Authored: Mon Mar 20 12:41:09 2017 +0100
Committer: Lee moon soo <m...@apache.org>
Committed: Tue Mar 21 11:13:46 2017 -0700

----------------------------------------------------------------------
 docs/rest-api/rest-notebook.md                  | 34 +++++++++++-
 .../apache/zeppelin/rest/NotebookRestApi.java   | 58 ++++++++++++--------
 .../rest/message/NewParagraphRequest.java       |  8 +++
 .../zeppelin/rest/ZeppelinRestApiTest.java      | 41 ++++++++++++--
 4 files changed, 111 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/89a1c53f/docs/rest-api/rest-notebook.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md
index 5a09450..1c5ebde 100644
--- a/docs/rest-api/rest-notebook.md
+++ b/docs/rest-api/rest-notebook.md
@@ -111,7 +111,19 @@ Notebooks REST API supports the following operations: 
List, Create, Get, Delete,
     },
     {
       "title": "paragraph title2",
-      "text": "paragraph text2"
+      "text": "paragraph text2",
+      "config": {
+        "title": true,
+        "colWidth": 6.0,
+        "results": [
+          {
+            "graph": {
+              "mode": "scatterChart",
+              "optionOpen": true
+            }
+          }
+        ]
+      }
     }
   ]
 }</pre></td>
@@ -601,6 +613,26 @@ Notebooks REST API supports the following operations: 
List, Create, Get, Delete,
 }</pre></td>
     </tr>
     <tr>
+      <td> sample JSON input (providing paragraph config) </td>
+      <td><pre>
+{
+  "title": "paragraph title2",
+  "text": "paragraph text2",
+  "config": {
+    "title": true,
+    "colWidth": 6.0,
+    "results": [
+      {
+        "graph": {
+          "mode": "pieChart",
+          "optionOpen": true
+        }
+      }
+    ]
+  }
+}</pre></td>
+    </tr>
+    <tr>
       <td> sample JSON response </td>
       <td><pre>
 {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/89a1c53f/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
----------------------------------------------------------------------
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 8292fd0..8c1075e 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
@@ -18,11 +18,7 @@
 package org.apache.zeppelin.rest;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
@@ -337,16 +333,16 @@ public class NotebookRestApi {
   @Path("/")
   @ZeppelinApi
   public Response createNote(String message) throws IOException {
+    String user = SecurityUtils.getPrincipal();
     LOG.info("Create new note by JSON {}", message);
     NewNoteRequest request = gson.fromJson(message, NewNoteRequest.class);
-    AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
+    AuthenticationInfo subject = new AuthenticationInfo(user);
     Note note = notebook.createNote(subject);
     List<NewParagraphRequest> initialParagraphs = request.getParagraphs();
     if (initialParagraphs != null) {
       for (NewParagraphRequest paragraphRequest : initialParagraphs) {
         Paragraph p = note.addParagraph(subject);
-        p.setTitle(paragraphRequest.getTitle());
-        p.setText(paragraphRequest.getText());
+        initParagraph(p, paragraphRequest, user);
       }
     }
     note.addParagraph(subject); // add one paragraph to the last
@@ -425,6 +421,7 @@ public class NotebookRestApi {
   @ZeppelinApi
   public Response insertParagraph(@PathParam("noteId") String noteId, String 
message)
       throws IOException {
+    String user = SecurityUtils.getPrincipal();
     LOG.info("insert paragraph {} {}", noteId, message);
 
     Note note = notebook.getNote(noteId);
@@ -432,7 +429,7 @@ public class NotebookRestApi {
     checkIfUserCanWrite(noteId, "Insufficient privileges you cannot add 
paragraph to this note");
 
     NewParagraphRequest request = gson.fromJson(message, 
NewParagraphRequest.class);
-    AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
+    AuthenticationInfo subject = new AuthenticationInfo(user);
     Paragraph p;
     Double indexDouble = request.getIndex();
     if (indexDouble == null) {
@@ -440,9 +437,7 @@ public class NotebookRestApi {
     } else {
       p = note.insertParagraph(indexDouble.intValue(), subject);
     }
-    p.setTitle(request.getTitle());
-    p.setText(request.getText());
-
+    initParagraph(p, request, user);
     note.persist(subject);
     notebookServer.broadcastNote(note);
     return new JsonResponse<>(Status.OK, "", p.getId()).build();
@@ -486,17 +481,7 @@ public class NotebookRestApi {
     checkIfParagraphIsNotNull(p);
 
     Map<String, Object> newConfig = gson.fromJson(message, HashMap.class);
-    if (newConfig == null || newConfig.isEmpty()) {
-      LOG.warn("{} is trying to update paragraph {} of note {} with empty 
config",
-          user, paragraphId, noteId);
-      throw new BadRequestException("paragraph config cannot be empty");
-    }
-    Map<String, Object> origConfig = p.getConfig();
-    for (String key : newConfig.keySet()) {
-      origConfig.put(key, newConfig.get(key));
-    }
-
-    p.setConfig(origConfig);
+    configureParagraph(p, newConfig, user);
     AuthenticationInfo subject = new AuthenticationInfo(user);
     note.persist(subject);
 
@@ -963,4 +948,31 @@ public class NotebookRestApi {
     }
   }
 
+  private void initParagraph(Paragraph p, NewParagraphRequest request, String 
user)
+      throws IOException {
+    LOG.info("Init Paragraph for user {}", user);
+    checkIfParagraphIsNotNull(p);
+    p.setTitle(request.getTitle());
+    p.setText(request.getText());
+    Map< String, Object > config = request.getConfig();
+    if ( config != null && !config.isEmpty()) {
+      configureParagraph(p, config, user);
+    }
+  }
+
+  private void configureParagraph(Paragraph p, Map< String, Object> newConfig, 
String user)
+      throws IOException {
+    LOG.info("Configure Paragraph for user {}", user);
+    if (newConfig == null || newConfig.isEmpty()) {
+      LOG.warn("{} is trying to update paragraph {} of note {} with empty 
config",
+              user, p.getId(), p.getNote().getId());
+      throw new BadRequestException("paragraph config cannot be empty");
+    }
+    Map<String, Object> origConfig = p.getConfig();
+    for (String key : newConfig.keySet()) {
+      origConfig.put(key, newConfig.get(key));
+    }
+    p.setConfig(origConfig);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/89a1c53f/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/NewParagraphRequest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/NewParagraphRequest.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/NewParagraphRequest.java
index bde920b..5be732f 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/NewParagraphRequest.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/message/NewParagraphRequest.java
@@ -17,15 +17,21 @@
 
 package org.apache.zeppelin.rest.message;
 
+import java.util.HashMap;
+
 /**
  * NewParagraphRequest rest api request message
  *
  * index field will be ignored when it's used to provide initial paragraphs
+ * visualization (optional) one of:
+ * table,pieChart,multibarChart,stackedAreaChart,lineChart,scatterChart
+ * colWidth (optional), e.g. 12.0
  */
 public class NewParagraphRequest {
   String title;
   String text;
   Double index;
+  HashMap< String, Object > config;
 
   public NewParagraphRequest() {
 
@@ -42,4 +48,6 @@ public class NewParagraphRequest {
   public Double getIndex() {
     return index;
   }
+
+  public HashMap< String, Object > getConfig() { return config; }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/89a1c53f/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
----------------------------------------------------------------------
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 e7b95e4..d53c3b5 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
@@ -133,8 +133,11 @@ public class ZeppelinRestApiTest extends 
AbstractTestRestApi {
     String noteName = "test";
     String jsonRequest = "{\"name\":\"" + noteName + "\", \"paragraphs\": [" +
         "{\"title\": \"title1\", \"text\": \"text1\"}," +
-        "{\"title\": \"title2\", \"text\": \"text2\"}" +
-        "]}";
+        "{\"title\": \"title2\", \"text\": \"text2\"}," +
+        "{\"title\": \"titleConfig\", \"text\": \"text3\", " +
+        "\"config\": {\"colWidth\": 9.0, \"title\": true, "+
+        "\"results\": [{\"graph\": {\"mode\": \"pieChart\"}}] "+
+        "}}]} ";
     PostMethod post = httpPost("/notebook/", jsonRequest);
     LOG.info("testNoteCreate \n" + post.getResponseBodyAsString());
     assertThat("test note create method:", post, isAllowed());
@@ -154,13 +157,20 @@ public class ZeppelinRestApiTest extends 
AbstractTestRestApi {
       expectedNoteName = "Note " + newNoteId;
     }
     assertEquals("compare note name", expectedNoteName, newNoteName);
-    assertEquals("initial paragraph check failed", 3, 
newNote.getParagraphs().size());
+    assertEquals("initial paragraph check failed", 4, 
newNote.getParagraphs().size());
     for (Paragraph p : newNote.getParagraphs()) {
       if (StringUtils.isEmpty(p.getText())) {
         continue;
       }
       assertTrue("paragraph title check failed", 
p.getTitle().startsWith("title"));
       assertTrue("paragraph text check failed", 
p.getText().startsWith("text"));
+      if ( p.getTitle() == "titleConfig"){
+        assertEquals("paragraph col width check failed", 9.0, 
p.getConfig().get("colWidth"));
+        assertTrue("paragraph show title check failed", ((boolean) 
p.getConfig().get("title")));
+        Map graph = ((List<Map>)p.getConfig().get("results")).get(0);
+        String mode = graph.get("mode").toString();
+        assertEquals("paragraph graph mode check failed", "pieChart", mode);
+      }
     }
     // cleanup
     ZeppelinServer.notebook.removeNote(newNoteId, anonymous);
@@ -213,8 +223,8 @@ public class ZeppelinRestApiTest extends 
AbstractTestRestApi {
 
 
   @Test
-  public void testexportNote() throws IOException {
-    LOG.info("testexportNote");
+  public void testExportNote() throws IOException {
+    LOG.info("testExportNote");
     Note note = ZeppelinServer.notebook.createNote(anonymous);
     assertNotNull("can't create new note", note);
     note.setName("source note for export");
@@ -246,7 +256,7 @@ public class ZeppelinRestApiTest extends 
AbstractTestRestApi {
   public void testImportNotebook() throws IOException {
     Map<String, Object> resp;
     String noteName = "source note for import";
-    LOG.info("testImortNote");
+    LOG.info("testImportNote");
     // create test note
     Note note = ZeppelinServer.notebook.createNote(anonymous);
     assertNotNull("can't create new note", note);
@@ -620,6 +630,25 @@ public class ZeppelinRestApiTest extends 
AbstractTestRestApi {
     assertEquals("title2", paragraphAtIdx0.getTitle());
     assertEquals("text2", paragraphAtIdx0.getText());
 
+    //append paragraph providing graph
+    String jsonRequest3 = "{\"title\": \"title3\", \"text\": \"text3\", "+
+                          "\"config\": {\"colWidth\": 9.0, \"title\": true, "+
+                          "\"results\": [{\"graph\": {\"mode\": 
\"pieChart\"}}]}}";
+    PostMethod post3 = httpPost("/notebook/" + note.getId() + "/paragraph", 
jsonRequest3);
+    LOG.info("testInsertParagraph response4\n" + 
post3.getResponseBodyAsString());
+    assertThat("Test insert method:", post3, isAllowed());
+    post3.releaseConnection();
+
+    Paragraph p = note.getLastParagraph();
+    assertEquals("title3", p.getTitle());
+    assertEquals("text3", p.getText());
+    Map result = ((List<Map>)p.getConfig().get("results")).get(0);
+    String mode = ((Map)result.get("graph")).get("mode").toString();
+    assertEquals("pieChart", mode);
+    assertEquals(9.0, p.getConfig().get("colWidth"));
+    assertTrue(((boolean) p.getConfig().get("title")));
+
+
     ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
   }
 

Reply via email to