diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md index 3c94268e707..a1e8de61fac 100644 --- a/docs/rest-api/rest-notebook.md +++ b/docs/rest-api/rest-notebook.md @@ -33,7 +33,7 @@ limitations under the License.
### Notebook REST API list - Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run as detailed in the following table + Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run, Export, Import as detailed in the following table @@ -773,3 +773,112 @@ limitations under the License.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Export notebook
DescriptionThis ```GET``` method exports a notebook by the given id and gernerates a JSON +
URL```http://[zeppelin-server]:[zeppelin-port]/api/notebook/export/[notebookId]```
Success code201
Fail code 500
sample JSON response
{
+  "paragraphs": [
+    {
+      "text": "%md This is my new paragraph in my new note",
+      "dateUpdated": "Jan 8, 2016 4:49:38 PM",
+      "config": {
+        "enabled": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1452300578795_1196072540",
+      "id": "20160108-164938_1685162144",
+      "dateCreated": "Jan 8, 2016 4:49:38 PM",
+      "status": "READY",
+      "progressUpdateIntervalMs": 500
+    }
+  ],
+  "name": "source note for export",
+  "id": "2B82H3RR1",
+  "angularObjects": {},
+  "config": {},
+  "info": {}
+}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Export notebook
DescriptionThis ```POST``` method imports a notebook from the notebook JSON input +
URL```http://[zeppelin-server]:[zeppelin-port]/api/notebook/import```
Success code201
Fail code 500
sample JSON input
{
+  "paragraphs": [
+    {
+      "text": "%md This is my new paragraph in my new note",
+      "dateUpdated": "Jan 8, 2016 4:49:38 PM",
+      "config": {
+        "enabled": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1452300578795_1196072540",
+      "id": "20160108-164938_1685162144",
+      "dateCreated": "Jan 8, 2016 4:49:38 PM",
+      "status": "READY",
+      "progressUpdateIntervalMs": 500
+    }
+  ],
+  "name": "source note for export",
+  "id": "2B82H3RR1",
+  "angularObjects": {},
+  "config": {},
+  "info": {}
+}
sample JSON response
"status": "CREATED","message": "","body": "2AZPHY918"}
\ No newline at end of file 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 7871da87e74..486e5b122c7 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 @@ -52,7 +52,9 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; - +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; +import java.io.StringReader; /** * Rest api endpoint for the noteBook. */ @@ -146,6 +148,34 @@ public Response getNotebook(@PathParam("notebookId") String notebookId) throws I return new JsonResponse<>(Status.OK, "", note).build(); } + /** + * export note REST API + * + * @param + * @return note JSON with status.OK + * @throws IOException + */ + @GET + @Path("export/{id}") + public Response exportNoteBook(@PathParam("id") String noteId) throws IOException { + String exportJson = notebook.exportNote(noteId); + return new JsonResponse(Status.OK, "", exportJson).build(); + } + + /** + * import new note REST API + * + * @param req - notebook Json + * @return JSON with new note ID + * @throws IOException + */ + @POST + @Path("import") + public Response importNotebook(String req) throws IOException { + Note newNote = notebook.importNote(req, null); + return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build(); + } + /** * Create new note REST API * @param message - JSON with new note name 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 64698fcdfd4..11fa7f1b9fe 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 @@ -494,56 +494,15 @@ private void cloneNote(NotebookSocket conn, Notebook notebook, Message fromMessa protected Note importNote(NotebookSocket conn, Notebook notebook, Message fromMessage) throws IOException { - - Note note = notebook.createNote(); + Note note = null; if (fromMessage != null) { String noteName = (String) ((Map) fromMessage.get("notebook")).get("name"); - if (noteName == null || noteName.isEmpty()) { - noteName = "Note " + note.getId(); - } - note.setName(noteName); - ArrayList paragraphs = ((Map) fromMessage.get("notebook")) - .get("paragraphs"); - if (paragraphs.size() > 0) { - for (Map paragraph : paragraphs) { - try { - Paragraph p = note.addParagraph(); - String text = (String) paragraph.get("text"); - p.setText(text); - p.setTitle((String) paragraph.get("title")); - Map params = (Map) ((Map) paragraph - .get("settings")).get("params"); - Map forms = (Map) ((Map) paragraph - .get("settings")).get("forms"); - if (params != null) { - p.settings.setParams(params); - } - if (forms != null) { - p.settings.setForms(forms); - } - Map result = (Map) paragraph.get("result"); - if (result != null) { - InterpreterResult.Code code = InterpreterResult.Code - .valueOf((String) result.get("code")); - InterpreterResult.Type type = InterpreterResult.Type - .valueOf((String) result.get("type")); - String msg = (String) result.get("msg"); - p.setReturn(new InterpreterResult(code, type, msg), null); - } - - Map config = (Map) paragraph - .get("config"); - p.setConfig(config); - } catch (Exception e) { - LOG.error("Exception while setting parameter in paragraph", e); - } - } - } + String noteJson = gson.toJson(fromMessage.get("notebook")); + note = notebook.importNote(noteJson, noteName); + note.persist(); + broadcastNote(note); + broadcastNoteList(); } - - note.persist(); - broadcastNote(note); - broadcastNoteList(); return note; } 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 c7ac6319f30..d8049cc6b80 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 @@ -319,6 +319,87 @@ public void testDeleteNoteBadId() throws IOException { } + @Test + public void testExportNotebook() throws IOException { + LOG.info("testExportNotebook"); + Note note = ZeppelinServer.notebook.createNote(); + assertNotNull("can't create new note", note); + note.setName("source note for export"); + Paragraph paragraph = note.addParagraph(); + Map config = paragraph.getConfig(); + config.put("enabled", true); + paragraph.setConfig(config); + paragraph.setText("%md This is my new paragraph in my new note"); + note.persist(); + String sourceNoteID = note.getId(); + // Call export Notebook REST API + GetMethod get = httpGet("/notebook/export/" + sourceNoteID); + LOG.info("testNotebookExport \n" + get.getResponseBodyAsString()); + assertThat("test notebook export method:", get, isAllowed()); + + Map resp = + gson.fromJson(get.getResponseBodyAsString(), + new TypeToken>() {}.getType()); + + String exportJSON = (String) resp.get("body"); + assertNotNull("Can not find new notejson", exportJSON); + LOG.info("export JSON:=" + exportJSON); + ZeppelinServer.notebook.removeNote(sourceNoteID); + get.releaseConnection(); + + } + + @Test + public void testImportNotebook() throws IOException { + Map resp; + String noteName = "source note for import"; + LOG.info("testImortNotebook"); + // create test notebook + Note note = ZeppelinServer.notebook.createNote(); + assertNotNull("can't create new note", note); + note.setName(noteName); + Paragraph paragraph = note.addParagraph(); + Map config = paragraph.getConfig(); + config.put("enabled", true); + paragraph.setConfig(config); + paragraph.setText("%md This is my new paragraph in my new note"); + note.persist(); + String sourceNoteID = note.getId(); + // get note content as JSON + String oldJson = getNoteContent(sourceNoteID); + // call notebook post + PostMethod importPost = httpPost("/notebook/import/", oldJson); + assertThat(importPost, isCreated()); + resp = + gson.fromJson(importPost.getResponseBodyAsString(), + new TypeToken>() {}.getType()); + String importId = (String) resp.get("body"); + + assertNotNull("Did not get back a notebook id in body", importId); + Note newNote = ZeppelinServer.notebook.getNote(importId); + assertEquals("Compare note names", noteName, newNote.getName()); + assertEquals("Compare paragraphs count", note.getParagraphs().size(), newNote.getParagraphs() + .size()); + // cleanup + ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(newNote.getId()); + importPost.releaseConnection(); + } + + private String getNoteContent(String id) throws IOException { + GetMethod get = httpGet("/notebook/export/" + id); + assertThat(get, isAllowed()); + get.addRequestHeader("Origin", "http://localhost"); + Map resp = + gson.fromJson(get.getResponseBodyAsString(), + new TypeToken>() {}.getType()); + assertEquals(200, get.getStatusCode()); + String body = resp.get("body").toString(); + // System.out.println("Body is " + body); + get.releaseConnection(); + return body; + } + private void testDeleteNotebook(String notebookId) throws IOException { DeleteMethod delete = httpDelete(("/notebook/" + notebookId)); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java index a068cea8ed2..e58df0d8f22 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.notebook; import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -54,6 +55,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; /** * Collection of Notes. */ @@ -150,6 +154,58 @@ public Note createNote(List interpreterIds) throws IOException { note.persist(); return note; } + + /** + * Export existing note. + * @param noteId - the note ID to clone + * @return Note JSON + * @throws IOException, IllegalArgumentException + */ + public String exportNote(String noteId) throws IOException, IllegalArgumentException { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setPrettyPrinting(); + Gson gson = gsonBuilder.create(); + Note note = getNote(noteId); + if (note == null) { + throw new IllegalArgumentException(noteId + " not found"); + } + return gson.toJson(note); + } + + /** + * import JSON as a new note. + * @param sourceJson - the note JSON to import + * @param noteName - the name of the new note + * @return notebook ID + * @throws IOException + */ + public Note importNote(String sourceJson, String noteName) throws IOException { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setPrettyPrinting(); + Gson gson = gsonBuilder.create(); + JsonReader reader = new JsonReader(new StringReader(sourceJson)); + reader.setLenient(true); + Note newNote; + try { + Note oldNote = gson.fromJson(reader, Note.class); + newNote = createNote(); + if (noteName != null) + newNote.setName(noteName); + else + newNote.setName(oldNote.getName()); + List paragraphs = oldNote.getParagraphs(); + for (Paragraph p : paragraphs) { + newNote.addCloneParagraph(p); + } + + newNote.persist(); + } catch (IOException e) { + logger.error(e.toString(), e); + throw e; + } + + return newNote; + } /** * Clone existing note.