diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md index c7e17ea77d0..ed39a4ff8f7 100644 --- a/docs/rest-api/rest-notebook.md +++ b/docs/rest-api/rest-notebook.md @@ -450,12 +450,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
-### Run a paragraph +### Run a paragraph asynchronously - @@ -487,6 +487,56 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
DescriptionThis ```POST``` method runs the paragraph by given notebook and paragraph id. + This ```POST``` method runs the paragraph asynchronously by given notebook and paragraph id. This API always return SUCCESS even if the execution of the paragraph fails later because the API is asynchronous
+
+### Run a paragraph synchronously + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Description This ```POST``` method runs the paragraph synchronously by given notebook and paragraph id. This API can return SUCCESS or ERROR depending on the outcome of the paragraph execution +
URL```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]```
Success code200
Fail code 500
sample JSON input (optional, only needed when if you want to update dynamic form's value)
+{
+  "name": "name of new notebook",
+  "params": {
+    "formLabel1": "value1",
+    "formLabel2": "value2"
+  }
+}
sample JSON response
{"status": "OK"}
sample JSON error
+{
+   "status": "INTERNAL\_SERVER\_ERROR",
+   "body": {
+       "code": "ERROR",
+       "type": "TEXT",
+       "msg": "bash: -c: line 0: unexpected EOF while looking for matching ``'\nbash: -c: line 1: syntax error: unexpected end of file\nExitValue: 2"
+   }
+}
+
### Stop a paragraph @@ -922,4 +972,3 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
- \ 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 6a286a4e19c..d148428e498 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 @@ -567,7 +567,7 @@ public Response getNoteParagraphJobStatus(@PathParam("notebookId") String notebo } /** - * Run paragraph job REST API + * Run asynchronously paragraph job REST API * * @param message - JSON with params if user wants to update dynamic form's value * null, empty string, empty json if user doesn't want to update @@ -580,7 +580,7 @@ public Response getNoteParagraphJobStatus(@PathParam("notebookId") String notebo public Response runParagraph(@PathParam("notebookId") String notebookId, @PathParam("paragraphId") String paragraphId, String message) throws IOException, IllegalArgumentException { - LOG.info("run paragraph job {} {} {}", notebookId, paragraphId, message); + LOG.info("run paragraph job asynchronously {} {} {}", notebookId, paragraphId, message); Note note = notebook.getNote(notebookId); if (note == null) { @@ -593,22 +593,60 @@ public Response runParagraph(@PathParam("notebookId") String notebookId, } // handle params if presented - if (!StringUtils.isEmpty(message)) { - RunParagraphWithParametersRequest request = - gson.fromJson(message, RunParagraphWithParametersRequest.class); - Map paramsForUpdating = request.getParams(); - if (paramsForUpdating != null) { - paragraph.settings.getParams().putAll(paramsForUpdating); - AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); - note.setLastReplName(paragraph.getId()); - note.persist(subject); - } - } + handleParagraphParams(message, note, paragraph); note.run(paragraph.getId()); return new JsonResponse<>(Status.OK).build(); } +/** + * Run synchronously a paragraph REST API + * + * @param noteId - noteId + * @param paragraphId - paragraphId + * @param message - JSON with params if user wants to update dynamic form's value + * null, empty string, empty json if user doesn't want to update + * + * @return JSON with status.OK + * @throws IOException, IllegalArgumentException + */ + @POST + @Path("run/{notebookId}/{paragraphId}") + @ZeppelinApi + public Response runParagraphSynchronously(@PathParam("notebookId") String noteId, + @PathParam("paragraphId") String paragraphId, + String message) throws + IOException, IllegalArgumentException { + LOG.info("run paragraph synchronously {} {} {}", noteId, paragraphId, message); + + Note note = notebook.getNote(noteId); + if (note == null) { + return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build(); + } + + Paragraph paragraph = note.getParagraph(paragraphId); + if (paragraph == null) { + return new JsonResponse<>(Status.NOT_FOUND, "paragraph not found.").build(); + } + + // handle params if presented + handleParagraphParams(message, note, paragraph); + + if (paragraph.getListener() == null) { + note.initializeJobListenerForParagraph(paragraph); + } + + paragraph.run(); + + final InterpreterResult result = paragraph.getResult(); + + if (result.code() == InterpreterResult.Code.SUCCESS) { + return new JsonResponse<>(Status.OK, result).build(); + } else { + return new JsonResponse<>(Status.INTERNAL_SERVER_ERROR, result).build(); + } + } + /** * Stop(delete) paragraph job REST API * @@ -800,4 +838,21 @@ public Response search(@QueryParam("q") String queryTerm) { return new JsonResponse<>(Status.OK, notebooksFound).build(); } + + private void handleParagraphParams(String message, Note note, Paragraph paragraph) + throws IOException { + // handle params if presented + if (!StringUtils.isEmpty(message)) { + RunParagraphWithParametersRequest request = + gson.fromJson(message, RunParagraphWithParametersRequest.class); + Map paramsForUpdating = request.getParams(); + if (paramsForUpdating != null) { + paragraph.settings.getParams().putAll(paramsForUpdating); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + note.setLastReplName(paragraph.getId()); + note.persist(subject); + } + } + } + } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java index e2194fd6741..31df6181a2d 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java @@ -17,6 +17,8 @@ package org.apache.zeppelin.notebook; +import static java.lang.String.format; + import java.io.IOException; import java.io.Serializable; import java.util.HashMap; @@ -172,6 +174,28 @@ void setInterpreterFactory(InterpreterFactory factory) { } } + public void initializeJobListenerForParagraph(Paragraph paragraph) { + final Note paragraphNote = paragraph.getNote(); + if (paragraphNote.getId().equals(this.getId())) { + throw new IllegalArgumentException(format("The paragraph %s from note %s " + + "does not belong to note %s", paragraph.getId(), paragraphNote.getId(), + this.getId())); + } + + boolean foundParagraph = false; + for (Paragraph ownParagraph : paragraphs) { + if (paragraph.getId().equals(ownParagraph.getId())) { + paragraph.setListener(this.jobListenerFactory.getParagraphJobListener(this)); + foundParagraph = true; + } + } + + if (!foundParagraph) { + throw new IllegalArgumentException(format("Cannot find paragraph %s " + + "from note %s", paragraph.getId(), paragraphNote.getId())); + } + } + void setJobListenerFactory(JobListenerFactory jobListenerFactory) { this.jobListenerFactory = jobListenerFactory; } @@ -484,7 +508,7 @@ public void run(String paragraphId) { logger.debug("New paragraph: {}", pText); p.setEffectiveText(pText); } else { - String intpExceptionMsg = String.format("%s", + String intpExceptionMsg = format("%s", p.getJobName() + "'s Interpreter " + requiredReplName + " not found"