Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master 61bc27996 -> 2714d28b5


Rest Apis to export/import with common code base

1)  Rest apis to export notebook as JSON
2)  Rest api to import notebook from JSON
https://issues.apache.org/jira/browse/ZEPPELIN-590

Author: swakrish <[email protected]>
Author: Ramaswamy Devarajan <[email protected]>

Closes #614 from swakrish/master and squashes the following commits:

83e7a6d [swakrish] Merge pull request #7 from apache/master
042d9af [swakrish] Merge pull request #6 from apache/master
f5b0805 [swakrish] Merge branch 'master' of 
[email protected]:swakrish/incubator-zeppelin.git
869b48f [swakrish] logging the error
f8bf1f3 [swakrish] Merge pull request #5 from apache/master
f8a992c [swakrish] Merge branch 'master' of 
[email protected]:swakrish/incubator-zeppelin.git
270e17b [swakrish] changed soureJSON to sourceJson
4cb69be [swakrish] Merge pull request #4 from apache/master
e282958 [swakrish] Changed websocket import to use common code
e928c14 [swakrish] changed doc to make it http post
4be62f6 [swakrish] Merge branch 'master' of 
[email protected]:swakrish/incubator-zeppelin.git
db6a580 [swakrish] changed HTTP Put to POST in the docs
ec14034 [swakrish] Merge pull request #3 from apache/master
630664c [swakrish] Merge pull request #2 from apache/master
b080d7d [swakrish] Merge pull request #1 from apache/master
db8b016 [Ramaswamy Devarajan] added note not found check for export
7351f31 [Ramaswamy Devarajan] Moved export/import methods to Notebook.java
6c19668 [Ramaswamy Devarajan] Changed http put to Http post for REST import
ecb8f1e [Ramaswamy Devarajan] Formatting for google style
9b64a66 [Ramaswamy Devarajan] fixed alignments
0e94dce [Ramaswamy Devarajan] add documentation
3645354 [Ramaswamy Devarajan] Rest Apis to export/import


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

Branch: refs/heads/master
Commit: 2714d28b5acf5fd5dc0f0135a6fea7b88c189756
Parents: 61bc279
Author: swakrish <[email protected]>
Authored: Thu Jan 21 16:16:14 2016 -0800
Committer: Alexander Bezzubov <[email protected]>
Committed: Fri Jan 22 18:37:54 2016 +0900

----------------------------------------------------------------------
 docs/rest-api/rest-notebook.md                  | 111 ++++++++++++++++++-
 .../apache/zeppelin/rest/NotebookRestApi.java   |  32 +++++-
 .../apache/zeppelin/socket/NotebookServer.java  |  53 +--------
 .../zeppelin/rest/ZeppelinRestApiTest.java      |  81 ++++++++++++++
 .../org/apache/zeppelin/notebook/Notebook.java  |  56 ++++++++++
 5 files changed, 284 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/2714d28b/docs/rest-api/rest-notebook.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md
index 3c94268..a1e8de6 100644
--- a/docs/rest-api/rest-notebook.md
+++ b/docs/rest-api/rest-notebook.md
@@ -33,7 +33,7 @@ limitations under the License.
  <br />
 ### 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 
   
   <table class="table-configuration">
     <col width="200">
@@ -773,3 +773,112 @@ limitations under the License.
     </tr>
   </table>
   
+
+  
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <th>Export notebook</th>
+      <th></th>
+    </tr>
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method exports a notebook by the given id and 
gernerates a JSON
+      </td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/export/[notebookId]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>201</td>
+    </tr>
+    <tr>
+      <td> Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <td> sample JSON response </td>
+      <td><pre>{
+  "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": {}
+}</pre></td>
+    </tr>
+  </table>
+  
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <th>Export notebook</th>
+      <th></th>
+    </tr>
+    <tr>
+      <td>Description</td>
+      <td>This ```POST``` method imports a notebook from the notebook JSON 
input
+      </td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/import```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>201</td>
+    </tr>
+    <tr>
+      <td> Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <td> sample JSON input </td>
+      <td><pre>{
+  "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": {}
+}</pre></td>
+<tr>
+      <td> sample JSON response </td>
+      <td><pre>"status": "CREATED","message": "","body": 
"2AZPHY918"}</pre></td>
+    </tr>
+    </tr>
+  </table>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/2714d28b/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 7871da8..486e5b1 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 org.slf4j.LoggerFactory;
 
 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.
  */
@@ -147,6 +149,34 @@ public class NotebookRestApi {
   }
 
   /**
+   * 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
    * @return JSON with new note ID

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/2714d28b/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 64698fc..11fa7f1 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 @@ public class NotebookServer extends WebSocketServlet 
implements
 
   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<Map> paragraphs = ((Map<String, ArrayList>) 
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<String, Object> params = (Map<String, Object>) ((Map) paragraph
-                .get("settings")).get("params");
-            Map<String, Input> forms = (Map<String, Input>) ((Map) paragraph
-                .get("settings")).get("forms");
-            if (params != null) {
-              p.settings.setParams(params);
-            }
-            if (forms != null) {
-              p.settings.setForms(forms);
-            }
-            Map<String, Object> 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<String, Object> config = (Map<String, Object>) 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;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/2714d28b/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 c7ac631..d8049cc 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 class ZeppelinRestApiTest extends 
AbstractTestRestApi {
   }
 
 
+  @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<String, Object> resp =
+        gson.fromJson(get.getResponseBodyAsString(),
+            new TypeToken<Map<String, Object>>() {}.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<String, Object> 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<Map<String, Object>>() {}.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<String, Object> resp =
+        gson.fromJson(get.getResponseBodyAsString(),
+            new TypeToken<Map<String, Object>>() {}.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));

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/2714d28b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
----------------------------------------------------------------------
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 a068cea..e58df0d 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.quartz.impl.StdSchedulerFactory;
 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 class Notebook {
     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<Paragraph> 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.

Reply via email to