Skip to content

Commit 2b2f8dc

Browse files
committed
Merge branch 'master' into notebook-search
Conflicts: zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
2 parents 44235eb + 16e921b commit 2b2f8dc

File tree

4 files changed

+266
-8
lines changed

4 files changed

+266
-8
lines changed

docs/rest-api/rest-notebook.md

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ limitations under the License.
3333
<br />
3434
### Notebook REST API list
3535

36-
Notebooks REST API supports the following operations: List, Create, Delete & Clone as detailed in the following table
36+
Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run as detailed in the following table
3737

3838
<table class="table-configuration">
3939
<col width="200">
@@ -119,6 +119,108 @@ limitations under the License.
119119
</tr>
120120
</table>
121121

122+
<br/>
123+
124+
<table class="table-configuration">
125+
<col width="200">
126+
<tr>
127+
<th>Get notebook</th>
128+
<th></th>
129+
</tr>
130+
<tr>
131+
<td>Description</td>
132+
<td>This ```GET``` method retrieves an existing notebook's information using the given id.
133+
The body field of the returned JSON contain information about paragraphs in the notebook.
134+
</td>
135+
</tr>
136+
<tr>
137+
<td>URL</td>
138+
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]```</td>
139+
</tr>
140+
<tr>
141+
<td>Success code</td>
142+
<td>200</td>
143+
</tr>
144+
<tr>
145+
<td> Fail code</td>
146+
<td> 500 </td>
147+
</tr>
148+
<tr>
149+
<td> sample JSON response </td>
150+
<td><pre>
151+
{
152+
"status": "OK",
153+
"message": "",
154+
"body": {
155+
"paragraphs": [
156+
{
157+
"text": "%sql \nselect age, count(1) value\nfrom bank \nwhere age < 30 \ngroup by age \norder by age",
158+
"config": {
159+
"colWidth": 4,
160+
"graph": {
161+
"mode": "multiBarChart",
162+
"height": 300,
163+
"optionOpen": false,
164+
"keys": [
165+
{
166+
"name": "age",
167+
"index": 0,
168+
"aggr": "sum"
169+
}
170+
],
171+
"values": [
172+
{
173+
"name": "value",
174+
"index": 1,
175+
"aggr": "sum"
176+
}
177+
],
178+
"groups": [],
179+
"scatter": {
180+
"xAxis": {
181+
"name": "age",
182+
"index": 0,
183+
"aggr": "sum"
184+
},
185+
"yAxis": {
186+
"name": "value",
187+
"index": 1,
188+
"aggr": "sum"
189+
}
190+
}
191+
}
192+
},
193+
"settings": {
194+
"params": {},
195+
"forms": {}
196+
},
197+
"jobName": "paragraph_1423500782552_-1439281894",
198+
"id": "20150210-015302_1492795503",
199+
"result": {
200+
"code": "SUCCESS",
201+
"type": "TABLE",
202+
"msg": "age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n"
203+
},
204+
"dateCreated": "Feb 10, 2015 1:53:02 AM",
205+
"dateStarted": "Jul 3, 2015 1:43:17 PM",
206+
"dateFinished": "Jul 3, 2015 1:43:23 PM",
207+
"status": "FINISHED",
208+
"progressUpdateIntervalMs": 500
209+
}
210+
],
211+
"name": "Zeppelin Tutorial",
212+
"id": "2A94M5J1Z",
213+
"angularObjects": {},
214+
"config": {
215+
"looknfeel": "default"
216+
},
217+
"info": {}
218+
}
219+
}
220+
</pre></td>
221+
</tr>
222+
</table>
223+
122224
<br/>
123225

124226
<table class="table-configuration">
@@ -308,6 +410,18 @@ limitations under the License.
308410
<td> Fail code</td>
309411
<td> 500 </td>
310412
</tr>
413+
<tr>
414+
<td> sample JSON input (optional, only needed when if you want to update dynamic form's value) </td>
415+
<td><pre>
416+
{
417+
"name": "name of new notebook",
418+
"params": {
419+
"formLabel1": "value1",
420+
"formLabel2": "value2"
421+
}
422+
}
423+
</pre></td>
424+
</tr>
311425
<tr>
312426
<td> sample JSON response </td>
313427
<td><pre>{"status":"OK"}</pre></td>

zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import javax.ws.rs.core.Response;
3434
import javax.ws.rs.core.Response.Status;
3535

36+
import org.apache.commons.lang3.StringUtils;
3637
import org.apache.zeppelin.interpreter.InterpreterSetting;
3738
import org.apache.zeppelin.notebook.Note;
3839
import org.apache.zeppelin.notebook.Notebook;
@@ -41,6 +42,7 @@
4142
import org.apache.zeppelin.rest.message.InterpreterSettingListForNoteBind;
4243
import org.apache.zeppelin.rest.message.NewNotebookRequest;
4344
import org.apache.zeppelin.rest.message.NewParagraphRequest;
45+
import org.apache.zeppelin.rest.message.RunParagraphWithParametersRequest;
4446
import org.apache.zeppelin.search.SearchService;
4547
import org.apache.zeppelin.server.JsonResponse;
4648
import org.apache.zeppelin.socket.NotebookServer;
@@ -133,6 +135,17 @@ public Response getNotebookList() throws IOException {
133135
return new JsonResponse<>(Status.OK, "", notesInfo ).build();
134136
}
135137

138+
@GET
139+
@Path("{notebookId}")
140+
public Response getNotebook(@PathParam("notebookId") String notebookId) throws IOException {
141+
Note note = notebook.getNote(notebookId);
142+
if (note == null) {
143+
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
144+
}
145+
146+
return new JsonResponse<>(Status.OK, "", note).build();
147+
}
148+
136149
/**
137150
* Create new note REST API
138151
* @param message - JSON with new note name
@@ -271,26 +284,43 @@ public Response getNoteJobStatus(@PathParam("notebookId") String notebookId) thr
271284

272285
/**
273286
* Run paragraph job REST API
274-
* @param
287+
*
288+
* @param message - JSON with params if user wants to update dynamic form's value
289+
* null, empty string, empty json if user doesn't want to update
290+
*
275291
* @return JSON with status.OK
276292
* @throws IOException, IllegalArgumentException
277293
*/
278294
@POST
279295
@Path("job/{notebookId}/{paragraphId}")
280296
public Response runParagraph(@PathParam("notebookId") String notebookId,
281-
@PathParam("paragraphId") String paragraphId) throws
297+
@PathParam("paragraphId") String paragraphId,
298+
String message) throws
282299
IOException, IllegalArgumentException {
283-
LOG.info("run paragraph job {} {} ", notebookId, paragraphId);
300+
LOG.info("run paragraph job {} {} {}", notebookId, paragraphId, message);
301+
284302
Note note = notebook.getNote(notebookId);
285303
if (note == null) {
286304
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
287305
}
288-
289-
if (note.getParagraph(paragraphId) == null) {
306+
307+
Paragraph paragraph = note.getParagraph(paragraphId);
308+
if (paragraph == null) {
290309
return new JsonResponse<>(Status.NOT_FOUND, "paragraph not found.").build();
291310
}
292311

293-
note.run(paragraphId);
312+
// handle params if presented
313+
if (!StringUtils.isEmpty(message)) {
314+
RunParagraphWithParametersRequest request = gson.fromJson(message,
315+
RunParagraphWithParametersRequest.class);
316+
Map<String, Object> paramsForUpdating = request.getParams();
317+
if (paramsForUpdating != null) {
318+
paragraph.settings.getParams().putAll(paramsForUpdating);
319+
note.persist();
320+
}
321+
}
322+
323+
note.run(paragraph.getId());
294324
return new JsonResponse<>(Status.OK).build();
295325
}
296326

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.zeppelin.rest.message;
19+
20+
import java.util.Map;
21+
22+
/**
23+
* RunParagraphWithParametersRequest rest api request message
24+
*/
25+
public class RunParagraphWithParametersRequest {
26+
Map<String, Object> params;
27+
28+
public RunParagraphWithParametersRequest() {
29+
30+
}
31+
32+
public Map<String, Object> getParams() {
33+
return params;
34+
}
35+
}

zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.zeppelin.notebook.Paragraph;
3535
import org.apache.zeppelin.rest.message.NewParagraphRequest;
3636
import org.apache.zeppelin.scheduler.Job.Status;
37+
import org.apache.zeppelin.server.JsonResponse;
3738
import org.apache.zeppelin.server.ZeppelinServer;
3839
import org.junit.AfterClass;
3940
import org.junit.BeforeClass;
@@ -193,6 +194,39 @@ public void testInterpreterRestart() throws IOException, InterruptedException {
193194
ZeppelinServer.notebook.removeNote(note.getId());
194195
}
195196

197+
@Test
198+
public void testGetNotebookInfo() throws IOException {
199+
LOG.info("testGetNotebookInfo");
200+
// Create note to get info
201+
Note note = ZeppelinServer.notebook.createNote();
202+
assertNotNull("can't create new note", note);
203+
note.setName("note");
204+
Paragraph paragraph = note.addParagraph();
205+
Map config = paragraph.getConfig();
206+
config.put("enabled", true);
207+
paragraph.setConfig(config);
208+
String paragraphText = "%md This is my new paragraph in my new note";
209+
paragraph.setText(paragraphText);
210+
note.persist();
211+
212+
String sourceNoteID = note.getId();
213+
GetMethod get = httpGet("/notebook/" + sourceNoteID);
214+
LOG.info("testGetNotebookInfo \n" + get.getResponseBodyAsString());
215+
assertThat("test notebook get method:", get, isAllowed());
216+
217+
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
218+
}.getType());
219+
220+
assertNotNull(resp);
221+
assertEquals("OK", resp.get("status"));
222+
223+
Map<String, Object> body = (Map<String, Object>) resp.get("body");
224+
List<Map<String, Object>> paragraphs = (List<Map<String, Object>>) body.get("paragraphs");
225+
226+
assertTrue(paragraphs.size() > 0);
227+
assertEquals(paragraphText, paragraphs.get(0).get("text"));
228+
}
229+
196230
@Test
197231
public void testNotebookCreateWithName() throws IOException {
198232
String noteName = "Test note name";
@@ -405,7 +439,52 @@ public void testNoteJobs() throws IOException, InterruptedException {
405439
//cleanup
406440
ZeppelinServer.notebook.removeNote(note.getId());
407441
}
408-
442+
443+
@Test
444+
public void testRunParagraphWithParams() throws IOException, InterruptedException {
445+
LOG.info("testRunParagraphWithParams");
446+
// Create note to run test.
447+
Note note = ZeppelinServer.notebook.createNote();
448+
assertNotNull("can't create new note", note);
449+
note.setName("note for run test");
450+
Paragraph paragraph = note.addParagraph();
451+
452+
Map config = paragraph.getConfig();
453+
config.put("enabled", true);
454+
paragraph.setConfig(config);
455+
456+
paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)");
457+
note.persist();
458+
String noteID = note.getId();
459+
460+
note.runAll();
461+
// wait until job is finished or timeout.
462+
int timeout = 1;
463+
while (!paragraph.isTerminated()) {
464+
Thread.sleep(1000);
465+
if (timeout++ > 120) {
466+
LOG.info("testRunParagraphWithParams timeout job.");
467+
break;
468+
}
469+
}
470+
471+
// Call Run paragraph REST API
472+
PostMethod postParagraph = httpPost("/notebook/job/" + noteID + "/" + paragraph.getId(),
473+
"{\"params\": {\"param\": \"hello\", \"param2\": \"world\"}}");
474+
assertThat("test paragraph run:", postParagraph, isAllowed());
475+
postParagraph.releaseConnection();
476+
Thread.sleep(1000);
477+
478+
Note retrNote = ZeppelinServer.notebook.getNote(noteID);
479+
Paragraph retrParagraph = retrNote.getParagraph(paragraph.getId());
480+
Map<String, Object> params = retrParagraph.settings.getParams();
481+
assertEquals("hello", params.get("param"));
482+
assertEquals("world", params.get("param2"));
483+
484+
//cleanup
485+
ZeppelinServer.notebook.removeNote(note.getId());
486+
}
487+
409488
@Test
410489
public void testCronJobs() throws InterruptedException, IOException{
411490
// create a note and a paragraph

0 commit comments

Comments
 (0)