mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge remote-tracking branch 'origin/master' into ZEPPELIN-1999
This commit is contained in:
commit
7c4489cfbf
21 changed files with 350 additions and 59 deletions
|
|
@ -106,6 +106,7 @@
|
|||
<li class="title"><span><b>REST API</b><span></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-interpreter.html">Interpreter API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-notebook.html">Notebook API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-notebookRepo.html">Notebook Repository API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-credential.html">Credential API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-helium.html">Helium API</a></li>
|
||||
|
|
|
|||
BIN
docs/assets/themes/zeppelin/img/pig_zeppelin_tutorial.png
Normal file
BIN
docs/assets/themes/zeppelin/img/pig_zeppelin_tutorial.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
|
|
@ -163,8 +163,10 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
|
|||
* REST API: available REST API list in Apache Zeppelin
|
||||
* [Interpreter API](./rest-api/rest-interpreter.html)
|
||||
* [Notebook API](./rest-api/rest-notebook.html)
|
||||
* [Notebook Repository API](./rest-api/rest-notebookRepo.html)
|
||||
* [Configuration API](./rest-api/rest-configuration.html)
|
||||
* [Credential API](./rest-api/rest-credential.html)
|
||||
* [Helium API](./rest-api/rest-helium.html)
|
||||
* Security: available security support in Apache Zeppelin
|
||||
* [Authentication for NGINX](./security/authentication.html)
|
||||
* [Shiro Authentication](./security/shiroauthentication.html)
|
||||
|
|
@ -179,6 +181,8 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
|
|||
* Contribute
|
||||
* [Writing Zeppelin Interpreter](./development/writingzeppelininterpreter.html)
|
||||
* [Writing Zeppelin Application (Experimental)](./development/writingzeppelinapplication.html)
|
||||
* [Writing Zeppelin Spell (Experimental)](./development/writingzeppelinspell.html)
|
||||
* [Writing Zeppelin Visualization (Experimental)](./development/writingzeppelinvisualization.html)
|
||||
* [How to contribute (code)](./development/howtocontribute.html)
|
||||
* [How to contribute (documentation website)](./development/howtocontributewebsite.html)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ group: manual
|
|||
[Apache Pig](https://pig.apache.org/) is a platform for analyzing large data sets that consists of a high-level language for expressing data analysis programs, coupled with infrastructure for evaluating these programs. The salient property of Pig programs is that their structure is amenable to substantial parallelization, which in turns enables them to handle very large data sets.
|
||||
|
||||
## Supported interpreter type
|
||||
- `%pig.script` (default)
|
||||
- `%pig.script` (default Pig interpreter, so you can use `%pig`)
|
||||
|
||||
All the pig script can run in this type of interpreter, and display type is plain text.
|
||||
`%pig.script` is like the Pig grunt shell. Anything you can run in Pig grunt shell can be run in `%pig.script` interpreter, it is used for running Pig script where you don’t need to visualize the data, it is suitable for data munging.
|
||||
|
||||
- `%pig.query`
|
||||
|
||||
Almost the same as `%pig.script`. The only difference is that you don't need to add alias in the last statement. And the display type is table.
|
||||
|
||||
`%pig.query` is a little different compared with `%pig.script`. It is used for exploratory data analysis via Pig latin where you can leverage Zeppelin’s visualization ability. There're 2 minor differences in the last statement between `%pig.script` and `%pig.query`
|
||||
- No pig alias in the last statement in `%pig.query` (read the examples below).
|
||||
- The last statement must be in single line in `%pig.query`
|
||||
|
||||
## Supported runtime mode
|
||||
- Local
|
||||
- MapReduce
|
||||
|
|
@ -52,8 +54,8 @@ group: manual
|
|||
### How to configure interpreter
|
||||
|
||||
At the Interpreters menu, you have to create a new Pig interpreter. Pig interpreter has below properties by default.
|
||||
And you can set any pig properties here which will be passed to pig engine. (like tez.queue.name & mapred.job.queue.name).
|
||||
Besides, we use paragraph title as job name if it exists, else use the last line of pig script. So you can use that to find app running in YARN RM UI.
|
||||
And you can set any Pig properties here which will be passed to Pig engine. (like tez.queue.name & mapred.job.queue.name).
|
||||
Besides, we use paragraph title as job name if it exists, else use the last line of Pig script. So you can use that to find app running in YARN RM UI.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -95,22 +97,52 @@ Besides, we use paragraph title as job name if it exists, else use the last line
|
|||
```
|
||||
%pig
|
||||
|
||||
raw_data = load 'dataset/sf_crime/train.csv' using PigStorage(',') as (Dates,Category,Descript,DayOfWeek,PdDistrict,Resolution,Address,X,Y);
|
||||
b = group raw_data all;
|
||||
c = foreach b generate COUNT($1);
|
||||
dump c;
|
||||
bankText = load 'bank.csv' using PigStorage(';');
|
||||
bank = foreach bankText generate $0 as age, $1 as job, $2 as marital, $3 as education, $5 as balance;
|
||||
bank = filter bank by age != '"age"';
|
||||
bank = foreach bank generate (int)age, REPLACE(job,'"','') as job, REPLACE(marital, '"', '') as marital, (int)(REPLACE(balance, '"', '')) as balance;
|
||||
store bank into 'clean_bank.csv' using PigStorage(';'); -- this statement is optional, it just show you that most of time %pig.script is used for data munging before querying the data.
|
||||
```
|
||||
|
||||
##### pig.query
|
||||
|
||||
Get the number of each age where age is less than 30
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
b = foreach raw_data generate Category;
|
||||
c = group b by Category;
|
||||
foreach c generate group as category, COUNT($1) as count;
|
||||
|
||||
bank_data = filter bank by age < 30;
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1);
|
||||
```
|
||||
|
||||
The same as above, but use dynamic text form so that use can specify the variable maxAge in textbox. (See screenshot below). Dynamic form is a very cool feature of Zeppelin, you can refer this [link]((../manual/dynamicform.html)) for details.
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
bank_data = filter bank by age < ${maxAge=40};
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1) as count;
|
||||
```
|
||||
|
||||
Get the number of each age for specific marital type, also use dynamic form here. User can choose the marital type in the dropdown list (see screenshot below).
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
bank_data = filter bank by marital=='${marital=single,single|divorced|married}';
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1) as count;
|
||||
```
|
||||
|
||||
The above examples are in the Pig tutorial note in Zeppelin, you can check that for details. Here's the screenshot.
|
||||
|
||||
<img class="img-responsive" width="1024px" style="margin:0 auto; padding: 26px;" src="../assets/themes/zeppelin/img/pig_zeppelin_tutorial.png" />
|
||||
|
||||
|
||||
Data is shared between `%pig` and `%pig.query`, so that you can do some common work in `%pig`, and do different kinds of query based on the data of `%pig`.
|
||||
Besides, we recommend you to specify alias explicitly so that the visualization can display the column name correctly. Here, we name `COUNT($1)` as `count`, if you don't do this,
|
||||
then we will name it using position, here we will use `col_1` to represent `COUNT($1)` if you don't specify alias for it. There's one pig tutorial note in zeppelin for your reference.
|
||||
Besides, we recommend you to specify alias explicitly so that the visualization can display the column name correctly. In the above example 2 and 3 of `%pig.query`, we name `COUNT($1)` as `count`. If you don't do this,
|
||||
then we will name it using position. E.g. in the above first example of `%pig.query`, we will use `col_1` in chart to represent `COUNT($1)`.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
"enabled": false
|
||||
}
|
||||
],
|
||||
"zeppelin_horizontalbar": [
|
||||
"zeppelin\_horizontalbar": [
|
||||
{
|
||||
"registry": "local",
|
||||
"pkg": {
|
||||
|
|
|
|||
179
docs/rest-api/rest-notebookRepo.md
Normal file
179
docs/rest-api/rest-notebookRepo.md
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Apache Zeppelin notebook repository REST API"
|
||||
description: "This page contains Apache Zeppelin notebook repository REST API information."
|
||||
group: rest-api
|
||||
---
|
||||
<!--
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
# Apache Zeppelin Notebook Repository API
|
||||
|
||||
<div id="toc"></div>
|
||||
|
||||
## Overview
|
||||
Apache Zeppelin provides several REST APIs for interaction and remote activation of zeppelin functionality.
|
||||
All REST APIs are available starting with the following endpoint `http://[zeppelin-server]:[zeppelin-port]/api`.
|
||||
Note that Apache Zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewers such as [JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc).
|
||||
|
||||
If you work with Apache Zeppelin and find a need for an additional REST API, please [file an issue or send us an email](http://zeppelin.apache.org/community.html).
|
||||
|
||||
## Notebook Repository REST API List
|
||||
|
||||
### List all available notebook repositories
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method returns all the available notebook repositories.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>500</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": "",
|
||||
"body": [
|
||||
{
|
||||
"name": "GitNotebookRepo",
|
||||
"className": "org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings": [
|
||||
{
|
||||
"type": "INPUT",
|
||||
"value": [],
|
||||
"selected": "ZEPPELIN_HOME/zeppelin/notebook/",
|
||||
"name": "Notebook Path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
||||
### Reload a notebook repository
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method triggers reloading and broadcasting of the note list.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories/reload```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>500</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": ""
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
||||
### Update a specific notebook repository
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```PUT``` method updates a specific notebook repository.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>
|
||||
404 when the specified notebook repository doesn't exist <br/>
|
||||
406 for invalid payload <br/>
|
||||
500 for any other errors
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON input</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"name":"org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings":{
|
||||
"Notebook Path":"/tmp/notebook/"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": "",
|
||||
"body": {
|
||||
"name": "GitNotebookRepo",
|
||||
"className": "org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings": [
|
||||
{
|
||||
"type": "INPUT",
|
||||
"value": [],
|
||||
"selected": "/tmp/notebook/",
|
||||
"name": "Notebook Path"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -83,7 +83,7 @@ public class ElasticsearchInterpreter extends Interpreter {
|
|||
+ " . same comments as for the search\n"
|
||||
+ " - get /index/type/id\n"
|
||||
+ " - delete /index/type/id\n"
|
||||
+ " - index /ndex/type/id <json-formatted document>\n"
|
||||
+ " - index /index/type/id <json-formatted document>\n"
|
||||
+ " . the id can be omitted, elasticsearch will generate one";
|
||||
|
||||
protected static final List<String> COMMANDS = Arrays.asList(
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@
|
|||
{
|
||||
"text": "%pig\n\nbankText \u003d load \u0027bank.csv\u0027 using PigStorage(\u0027;\u0027);\nbank \u003d foreach bankText generate $0 as age, $1 as job, $2 as marital, $3 as education, $5 as balance; \nbank \u003d filter bank by age !\u003d \u0027\"age\"\u0027;\nbank \u003d foreach bank generate (int)age, REPLACE(job,\u0027\"\u0027,\u0027\u0027) as job, REPLACE(marital, \u0027\"\u0027, \u0027\u0027) as marital, (int)(REPLACE(balance, \u0027\"\u0027, \u0027\u0027)) as balance;\n\n-- The following statement is optional, it depends on whether your needs.\n-- store bank into \u0027clean_bank.csv\u0027 using PigStorage(\u0027;\u0027);\n\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:11 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:08 PM",
|
||||
"config": {
|
||||
"colWidth": 12.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -138,15 +138,15 @@
|
|||
"jobName": "paragraph_1483277250237_-466604517",
|
||||
"id": "20161228-140640_1560978333",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:11 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:13 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:08 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:11 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c 30;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1);\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:16 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:13 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -183,15 +183,15 @@
|
|||
"jobName": "paragraph_1483277250238_-465450270",
|
||||
"id": "20161228-140730_1903342877",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:16 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:30 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:13 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:26 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c ${maxAge\u003d40};\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1);",
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c ${maxAge\u003d40};\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as count;",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:18 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:14 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -228,7 +228,7 @@
|
|||
"msg": [
|
||||
{
|
||||
"type": "TABLE",
|
||||
"data": "group\tcol_1\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n30\t150\n31\t199\n32\t224\n33\t186\n34\t231\n35\t180\n"
|
||||
"data": "group\tcount\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n30\t150\n31\t199\n32\t224\n33\t186\n34\t231\n35\t180\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -236,15 +236,15 @@
|
|||
"jobName": "paragraph_1483277250239_-465835019",
|
||||
"id": "20161228-154918_1551591203",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:18 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:32 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:14 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:29 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by marital\u003d\u003d\u0027${marital\u003dsingle,single|divorced|married}\u0027;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as c;\n\n\n",
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by marital\u003d\u003d\u0027${marital\u003dsingle,single|divorced|married}\u0027;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as count;\n\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:20 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:15 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -292,7 +292,7 @@
|
|||
"msg": [
|
||||
{
|
||||
"type": "TABLE",
|
||||
"data": "group\tc\n23\t3\n24\t11\n25\t11\n26\t18\n27\t26\n28\t23\n29\t37\n30\t56\n31\t104\n32\t105\n33\t103\n34\t142\n35\t109\n36\t117\n37\t100\n38\t99\n39\t88\n40\t105\n41\t97\n42\t91\n43\t79\n44\t68\n45\t76\n46\t82\n47\t78\n48\t91\n49\t87\n50\t74\n51\t63\n52\t66\n53\t75\n54\t56\n55\t68\n56\t50\n57\t78\n58\t67\n59\t56\n60\t36\n61\t15\n62\t5\n63\t7\n64\t6\n65\t4\n66\t7\n67\t5\n68\t1\n69\t5\n70\t5\n71\t5\n72\t4\n73\t6\n74\t2\n75\t3\n76\t1\n77\t5\n78\t2\n79\t3\n80\t6\n81\t1\n83\t2\n86\t1\n87\t1\n"
|
||||
"data": "group\tcount\n23\t3\n24\t11\n25\t11\n26\t18\n27\t26\n28\t23\n29\t37\n30\t56\n31\t104\n32\t105\n33\t103\n34\t142\n35\t109\n36\t117\n37\t100\n38\t99\n39\t88\n40\t105\n41\t97\n42\t91\n43\t79\n44\t68\n45\t76\n46\t82\n47\t78\n48\t91\n49\t87\n50\t74\n51\t63\n52\t66\n53\t75\n54\t56\n55\t68\n56\t50\n57\t78\n58\t67\n59\t56\n60\t36\n61\t15\n62\t5\n63\t7\n64\t6\n65\t4\n66\t7\n67\t5\n68\t1\n69\t5\n70\t5\n71\t5\n72\t4\n73\t6\n74\t2\n75\t3\n76\t1\n77\t5\n78\t2\n79\t3\n80\t6\n81\t1\n83\t2\n86\t1\n87\t1\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -300,8 +300,8 @@
|
|||
"jobName": "paragraph_1483277250240_-480070728",
|
||||
"id": "20161228-142259_575675591",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:30 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:34 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:27 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:31 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
|
|
|
|||
|
|
@ -75,6 +75,19 @@ public class NotebookRepoRestApi {
|
|||
return new JsonResponse<>(Status.OK, "", settings).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload notebook repository
|
||||
*/
|
||||
@GET
|
||||
@Path("reload")
|
||||
@ZeppelinApi
|
||||
public Response refreshRepo(){
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
LOG.info("Reloading notebook repository for user {}", subject.getUser());
|
||||
notebookWsServer.broadcastReloadedNoteList(subject, null);
|
||||
return new JsonResponse<>(Status.OK, "", null).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific note repo.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -200,9 +200,9 @@ public class NotebookServer extends WebSocketServlet
|
|||
messagereceived.ticket, ticket);
|
||||
} else {
|
||||
if (!messagereceived.op.equals(OP.PING)) {
|
||||
conn.send(serializeMessage(new Message(OP.ERROR_INFO).put("info",
|
||||
conn.send(serializeMessage(new Message(OP.SESSION_LOGOUT).put("info",
|
||||
"Your ticket is invalid possibly due to server restart. "
|
||||
+ "Please refresh the page and login again.")));
|
||||
+ "Please login again.")));
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -717,7 +717,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
LOG.info("Cannot {}. Connection readers {}. Allowed readers {}", op, userAndRoles, allowed);
|
||||
|
||||
conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info",
|
||||
"Insufficient privileges to " + op + "note.\n\n" + "Allowed users or roles: " + allowed
|
||||
"Insufficient privileges to " + op + " note.\n\n" + "Allowed users or roles: " + allowed
|
||||
.toString() + "\n\n" + "But the user " + userName + " belongs to: " + userAndRoles
|
||||
.toString())));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.util.regex.Pattern;
|
|||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.exec.DefaultExecutor;
|
||||
import org.apache.commons.exec.PumpStreamHandler;
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpMethodBase;
|
||||
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
||||
|
|
@ -333,7 +334,7 @@ public abstract class AbstractTestRestApi {
|
|||
GetMethod request = null;
|
||||
boolean isRunning = true;
|
||||
try {
|
||||
request = httpGet("/");
|
||||
request = httpGet("/version");
|
||||
isRunning = request.getStatusCode() == 200;
|
||||
} catch (IOException e) {
|
||||
LOG.error("AbstractTestRestApi.checkIfServerIsRunning() fails .. ZeppelinServer is not running");
|
||||
|
|
@ -427,8 +428,14 @@ public abstract class AbstractTestRestApi {
|
|||
httpClient.executeMethod(postMethod);
|
||||
LOG.info("{} - {}", postMethod.getStatusCode(), postMethod.getStatusText());
|
||||
Pattern pattern = Pattern.compile("JSESSIONID=([a-zA-Z0-9-]*)");
|
||||
java.util.regex.Matcher matcher = pattern.matcher(postMethod.getResponseHeaders("Set-Cookie")[0].toString());
|
||||
return matcher.find()? matcher.group(1) : StringUtils.EMPTY;
|
||||
Header[] setCookieHeaders = postMethod.getResponseHeaders("Set-Cookie");
|
||||
for (Header setCookie : setCookieHeaders) {
|
||||
java.util.regex.Matcher matcher = pattern.matcher(setCookie.toString());
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
protected static boolean userAndPasswordAreNotBlank(String user, String pwd) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.zeppelin.rest;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.anyOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -80,6 +81,13 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
List<Map<String, Object>> listOfRepositories = getListOfReposotiry();
|
||||
assertThat(listOfRepositories.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test public void reloadRepositories() throws IOException {
|
||||
GetMethod get = httpGet("/notebook-repositories/reload");
|
||||
int status = get.getStatusCode();
|
||||
get.releaseConnection();
|
||||
assertThat(status, is(200));
|
||||
}
|
||||
|
||||
@Test public void setNewDirectoryForLocalDirectory() throws IOException {
|
||||
List<Map<String, Object>> listOfRepositories = getListOfReposotiry();
|
||||
|
|
@ -95,7 +103,7 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
if (StringUtils.isBlank(localVfs)) {
|
||||
// no loval VFS set...
|
||||
// no local VFS set...
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +119,7 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
break;
|
||||
}
|
||||
}
|
||||
assertThat(updatedPath, is("/tmp/newDir"));
|
||||
assertThat(updatedPath, anyOf(is("/tmp/newDir"),is("/tmp/newDir/")));
|
||||
|
||||
// go back to normal
|
||||
payload = "{ \"name\": \"" + className + "\", \"settings\" : { \"Notebook Path\" : \"" + localVfs + "\" } }";
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();;
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
@ -50,21 +50,21 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
@Test
|
||||
public void testTicket() throws IOException {
|
||||
GetMethod get = httpGet("/security/ticket");
|
||||
GetMethod get = httpGet("/security/ticket", "admin", "password1");
|
||||
get.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
Map<String, String> body = (Map<String, String>) resp.get("body");
|
||||
collector.checkThat("Paramater principal", body.get("principal"),
|
||||
CoreMatchers.equalTo("anonymous"));
|
||||
CoreMatchers.equalTo("admin"));
|
||||
collector.checkThat("Paramater ticket", body.get("ticket"),
|
||||
CoreMatchers.equalTo("anonymous"));
|
||||
CoreMatchers.not("anonymous"));
|
||||
get.releaseConnection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserList() throws IOException {
|
||||
GetMethod get = httpGet("/security/userlist/admi");
|
||||
GetMethod get = httpGet("/security/userlist/admi", "admin", "password1");
|
||||
get.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
|
|
@ -75,7 +75,7 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
CoreMatchers.equalTo(true));
|
||||
get.releaseConnection();
|
||||
|
||||
GetMethod notUser = httpGet("/security/userlist/randomString");
|
||||
GetMethod notUser = httpGet("/security/userlist/randomString", "admin", "password1");
|
||||
notUser.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> notUserResp = gson.fromJson(notUser.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
|
|
@ -85,6 +85,5 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
notUser.releaseConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class DirAccessTest extends AbstractTestRestApi {
|
|||
public void testDirAccessForbidden() throws Exception {
|
||||
synchronized (this) {
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_SERVER_DEFAULT_DIR_ALLOWED.getVarName(), "false");
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
AbstractTestRestApi.startUp();
|
||||
HttpClient httpClient = new HttpClient();
|
||||
GetMethod getMethod = new GetMethod(getUrlToTest() + "/app/");
|
||||
httpClient.executeMethod(getMethod);
|
||||
|
|
@ -43,7 +43,7 @@ public class DirAccessTest extends AbstractTestRestApi {
|
|||
public void testDirAccessOk() throws Exception {
|
||||
synchronized (this) {
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_SERVER_DEFAULT_DIR_ALLOWED.getVarName(), "true");
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
AbstractTestRestApi.startUp();
|
||||
HttpClient httpClient = new HttpClient();
|
||||
GetMethod getMethod = new GetMethod(getUrlToTest() + "/app/");
|
||||
httpClient.executeMethod(getMethod);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
.when('/helium', {
|
||||
templateUrl: 'app/helium/helium.html',
|
||||
controller: 'HeliumCtrl'
|
||||
})
|
||||
})
|
||||
.when('/configuration', {
|
||||
templateUrl: 'app/configuration/configuration.html',
|
||||
controller: 'ConfigurationCtrl'
|
||||
|
|
@ -116,6 +116,24 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
timeout: 6000
|
||||
});
|
||||
})
|
||||
|
||||
//handel logout on API failure
|
||||
.config(function ($httpProvider, $provide) {
|
||||
$provide.factory('httpInterceptor', function ($q, $rootScope) {
|
||||
return {
|
||||
'responseError': function (rejection) {
|
||||
if (rejection.status === 405) {
|
||||
var data = {};
|
||||
data.info = '';
|
||||
$rootScope.$broadcast('session_logout', data);
|
||||
}
|
||||
$rootScope.$broadcast('httpResponseError', rejection);
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
});
|
||||
$httpProvider.interceptors.push('httpInterceptor');
|
||||
})
|
||||
.constant('TRASH_FOLDER_ID', '~Trash');
|
||||
|
||||
function auth() {
|
||||
|
|
|
|||
|
|
@ -267,7 +267,9 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
|
||||
$scope.renderDefaultDisplay = function(targetElemId, type, data, refresh) {
|
||||
if (type === DefaultDisplayType.TABLE) {
|
||||
$scope.renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
$timeout(function() {
|
||||
$scope.renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
}, 10);
|
||||
} else if (type === DefaultDisplayType.HTML) {
|
||||
renderHtml(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ANGULAR) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl);
|
||||
|
||||
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv) {
|
||||
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv, $location, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.SigningIn = false;
|
||||
|
|
@ -37,6 +37,17 @@ function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv)
|
|||
angular.element('#loginModal').modal('toggle');
|
||||
$rootScope.$broadcast('loginSuccess', true);
|
||||
$rootScope.userName = $scope.loginParams.userName;
|
||||
$scope.SigningIn = false;
|
||||
|
||||
//redirect to the page from where the user originally was
|
||||
if ($location.search() && $location.search()['ref']) {
|
||||
$timeout(function() {
|
||||
var redirectLocation = $location.search()['ref'];
|
||||
$location.$$search = {};
|
||||
$location.path(redirectLocation);
|
||||
}, 100);
|
||||
|
||||
}
|
||||
}, function errorCallback(errorResponse) {
|
||||
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.';
|
||||
$scope.SigningIn = false;
|
||||
|
|
@ -51,10 +62,25 @@ function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv)
|
|||
};
|
||||
};
|
||||
|
||||
//handle session logout message received from WebSocket
|
||||
$rootScope.$on('session_logout', function(event, data) {
|
||||
if ($rootScope.userName !== '') {
|
||||
$rootScope.userName = '';
|
||||
$rootScope.ticket = undefined;
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.loginParams = {};
|
||||
$scope.loginParams.errorText = data.info;
|
||||
angular.element('.nav-login-btn').click();
|
||||
}, 1000);
|
||||
var locationPath = $location.path();
|
||||
$location.path('/').search('ref', locationPath);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('initLoginValues', function() {
|
||||
initValues();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
|
|||
}
|
||||
}]
|
||||
});
|
||||
} else if (op === 'SESSION_LOGOUT') {
|
||||
$rootScope.$broadcast('session_logout', data);
|
||||
} else if (op === 'CONFIGURATIONS_INFO') {
|
||||
$rootScope.$broadcast('configurationsInfo', data);
|
||||
} else if (op === 'INTERPRETER_SETTINGS') {
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ public class InterpreterSettingManager {
|
|||
setting.getId());
|
||||
if (localRepoDir.exists()) {
|
||||
try {
|
||||
FileUtils.cleanDirectory(localRepoDir);
|
||||
FileUtils.forceDelete(localRepoDir);
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.info("A file that does not exist cannot be deleted, nothing to worry", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ public class Message {
|
|||
GET_INTERPRETER_SETTINGS, // [c-s] get interpreter settings
|
||||
INTERPRETER_SETTINGS, // [s-c] interpreter settings
|
||||
ERROR_INFO, // [s-c] error information to be sent
|
||||
SESSION_LOGOUT, // [s-c] error information to be sent
|
||||
WATCHER, // [s-c] Change websocket to watcher mode.
|
||||
PARAGRAPH_ADDED, // [s-c] paragraph is added
|
||||
PARAGRAPH_REMOVED, // [s-c] paragraph deleted
|
||||
|
|
|
|||
|
|
@ -1159,7 +1159,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
assertEquals(notebookAuthorization.getOwners(notePublic.getId()).size(), 1);
|
||||
assertEquals(notebookAuthorization.getReaders(notePublic.getId()).size(), 0);
|
||||
assertEquals(notebookAuthorization.getWriters(notePublic.getId()).size(), 0);
|
||||
|
||||
|
||||
// case of private note
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_PUBLIC.getVarName(), "false");
|
||||
ZeppelinConfiguration conf2 = ZeppelinConfiguration.create();
|
||||
|
|
@ -1181,8 +1181,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
notes2 = notebook.getAllNotes(user2);
|
||||
assertEquals(notes1.size(), 2);
|
||||
assertEquals(notes2.size(), 1);
|
||||
assertEquals(notes1.get(1).getId(), notePrivate.getId());
|
||||
|
||||
|
||||
// user1 have all rights
|
||||
assertEquals(notebookAuthorization.getOwners(notePrivate.getId()).size(), 1);
|
||||
assertEquals(notebookAuthorization.getReaders(notePrivate.getId()).size(), 1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue