diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md index 5a094508c9..766f38d0e5 100644 --- a/docs/rest-api/rest-notebook.md +++ b/docs/rest-api/rest-notebook.md @@ -111,7 +111,28 @@ Notebooks REST API supports the following operations: List, Create, Get, Delete, }, { "title": "paragraph title2", - "text": "paragraph text2" + "text": "paragraph text2", + "showTitle": true, + "colWidth": 9.0, + "graph": { + "mode": "pieChart" + } + } + { + "title": "paragraph title3", + "text": "paragraph text3", + "config": { + "title": true, + "colWidth": 6.0, + "results": [ + { + "graph": { + "mode": "scatterChart", + "optionOpen": true + } + } + ] + } } ] } @@ -598,6 +619,39 @@ Notebooks REST API supports the following operations: List, Create, Get, Delete, "title": "Paragraph insert revised", "text": "%spark\nprintln(\"Paragraph insert revised\")", "index": 0 +} + + + sample JSON input (providing graph,showTitle and colWidth information) +
+{
+  "title": "paragraph title2",
+  "text": "paragraph text2",
+  "showTitle": true,
+  "colWidth": 9.0,
+  "graph": {
+    "mode": "pieChart"
+  }
+ }
+ + + sample JSON input (providing paragraph config) +
+{
+  "title": "paragraph title3",
+  "text": "paragraph text3",
+  "config": {
+    "title": true,
+    "colWidth": 6.0,
+    "results": [
+      {
+        "graph": {
+          "mode": "scatterChart",
+          "optionOpen": true
+        }
+      }
+    ]
+  }
 }
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 8292fd067b..bb2ea07d11 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 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 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 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,62 @@ 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){ + Vector< String > ignoredFields = new Vector<>(); + if ( request.getGraph() != null) + ignoredFields.add("graph"); + if ( request.getColWidth() != null) + ignoredFields.add("colWidth"); + if ( request.getShowTitle() != null) + ignoredFields.add("showTitle"); + if ( ignoredFields.size() > 0 ) + LOG.warn("As config is provided, the following field(s) are ignored {}", + ignoredFields.toString()); + } + else { + config = new HashMap<>(); + Map graph = request.getGraph(); + if (graph != null) { + List results = new ArrayList<>(); + Map result = new HashMap<>(); + result.put("graph", graph); + results.add(result); + config.put("results", results); + } + Double colWidth = request.getColWidth(); + if (colWidth != null) { + config.put("colWidth", colWidth); + } + Boolean showTitle = request.getShowTitle(); + if (showTitle != null) { + config.put("title", showTitle); + } + } + if ( !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 origConfig = p.getConfig(); + for (String key : newConfig.keySet()) { + origConfig.put(key, newConfig.get(key)); + } + p.setConfig(origConfig); + } + } 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 bde920b833..238ec4d4de 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,24 @@ 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 > graph; + Boolean showTitle; + Double colWidth; + HashMap< String, Object > config; public NewParagraphRequest() { @@ -42,4 +51,12 @@ public class NewParagraphRequest { public Double getIndex() { return index; } + + public HashMap< String, Object > getGraph() { return graph; } + + public Boolean getShowTitle() { return showTitle; } + + public Double getColWidth() { return colWidth; } + + public HashMap< String, Object > getConfig() { return config; } } 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 e7b95e478d..5ebca2d9a0 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,14 @@ 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\": \"titleConfig1\", \"text\": \"text3\", " + + "\"graph\": {\"mode\": \"pieChart\"}, " + + "\"colWidth\": 9.0, \"showTitle\": true}, " + + "{\"title\": \"titleConfig2\", \"text\": \"text4\", " + + "\"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 +160,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", 5, 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)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 +226,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 +259,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { public void testImportNotebook() throws IOException { Map 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 +633,43 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi { assertEquals("title2", paragraphAtIdx0.getTitle()); assertEquals("text2", paragraphAtIdx0.getText()); + //append paragraph providing config + String jsonRequest3 = "{\"title\": \"title3\", \"text\": \"text3\", " + + "\"graph\": {\"mode\": \"pieChart\"}, " + + "\"colWidth\": 9.0, \"showTitle\": true}"; + PostMethod post3 = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest3); + LOG.info("testInsertParagraph response3\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)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"))); + + //append paragraph providing graph + String jsonRequest4 = "{\"title\": \"title4\", \"text\": \"text4\", "+ + "\"config\": {\"colWidth\": 9.0, \"title\": true, "+ + "\"results\": [{\"graph\": {\"mode\": \"pieChart\"}}]}}"; + PostMethod post4 = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest4); + LOG.info("testInsertParagraph response4\n" + post4.getResponseBodyAsString()); + assertThat("Test insert method:", post4, isAllowed()); + post4.releaseConnection(); + + Paragraph p2 = note.getLastParagraph(); + assertEquals("title4", p2.getTitle()); + assertEquals("text4", p2.getText()); + Map result2 = ((List)p.getConfig().get("results")).get(0); + String mode2 = ((Map)result.get("graph")).get("mode").toString(); + assertEquals("pieChart", mode2); + assertEquals(9.0, p2.getConfig().get("colWidth")); + assertTrue(((boolean) p2.getConfig().get("title"))); + + ZeppelinServer.notebook.removeNote(note.getId(), anonymous); }