mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
rebase and entity name convention.
This commit is contained in:
commit
df1b1dcae8
116 changed files with 3026 additions and 1715 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 364 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 280 KiB |
|
|
@ -62,7 +62,7 @@ When you are ready, just make a pull-request.
|
|||
|
||||
## Alternative way
|
||||
|
||||
You can directly edit `.md` files in `/docs/` directory at the web interface of github and make pull-request immediatly.
|
||||
You can directly edit `.md` files in `/docs/` directory at the web interface of github and make pull-request immediately.
|
||||
|
||||
## Stay involved
|
||||
Contributors should join the Zeppelin mailing lists.
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ Congratulations, you have successfully installed Apache Zeppelin! Here are two n
|
|||
* If you need more configuration for Apache Zeppelin, jump to the next section: [Apache Zeppelin Configuration](#apache-zeppelin-configuration).
|
||||
|
||||
#### If you need more information about Spark or JDBC interpreter settings...
|
||||
* Apache Zeppelin provides deep integration with [Apache Spark](http://spark.apache.org/). For more informtation, see [Spark Interpreter for Apache Zeppelin](../interpreter/spark.html).
|
||||
* Apache Zeppelin provides deep integration with [Apache Spark](http://spark.apache.org/). For more information, see [Spark Interpreter for Apache Zeppelin](../interpreter/spark.html).
|
||||
* You can also use generic JDBC connections in Apache Zeppelin. Go to [Generic JDBC Interpreter for Apache Zeppelin](../interpreter/jdbc.html).
|
||||
|
||||
#### If you are in a multi-user environment...
|
||||
|
|
@ -311,13 +311,13 @@ You can configure Apache Zeppelin with either **environment variables** in `conf
|
|||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN</td>
|
||||
<td>zeppelin.notebook.homescreen</td>
|
||||
<td></td>
|
||||
<td>Display notebook IDs on the Apache Zeppelin homescreen <br />i.e. 2A94M5J1Z</td>
|
||||
<td>Display note IDs on the Apache Zeppelin homescreen <br />i.e. 2A94M5J1Z</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE</td>
|
||||
<td>zeppelin.notebook.homescreen.hide</td>
|
||||
<td>false</td>
|
||||
<td>Hide the notebook ID set by <code>ZEPPELIN_NOTEBOOK_HOMESCREEN</code> on the Apache Zeppelin homescreen. <br />For the further information, please read <a href="../manual/notebookashomepage.html">Customize your Zeppelin homepage</a>.</td>
|
||||
<td>Hide the note ID set by <code>ZEPPELIN_NOTEBOOK_HOMESCREEN</code> on the Apache Zeppelin homescreen. <br />For the further information, please read <a href="../manual/notebookashomepage.html">Customize your Zeppelin homepage</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_WAR_TEMPDIR</td>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ into a directory on your host machine, or directly in your virtual machine.
|
|||
|
||||
Cloning Zeppelin into the `/scripts/vagrant/zeppelin-dev` directory from the host, will allow the directory to be shared between your host and the guest machine.
|
||||
|
||||
Cloning the project again may seem counter intuitive, since this script likley originated from the project repository. Consider copying just the vagrant/zeppelin-dev script from the Zeppelin project as a stand alone directory, then once again clone the specific branch you wish to build.
|
||||
Cloning the project again may seem counter intuitive, since this script likely originated from the project repository. Consider copying just the vagrant/zeppelin-dev script from the Zeppelin project as a stand alone directory, then once again clone the specific branch you wish to build.
|
||||
|
||||
Synced folders enable Vagrant to sync a folder on the host machine to the guest machine, allowing you to continue working on your project's files on your host machine, but use the resources in the guest machine to compile or run your project. _[(1) Synced Folder Description from Vagrant Up](https://docs.vagrantup.com/v2/synced-folders/index.html)_
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ import matplotlib.pyplot as plt
|
|||
import numpy as np
|
||||
import StringIO
|
||||
|
||||
# clear out any previous plots on this notebook
|
||||
# clear out any previous plots on this note
|
||||
plt.clf()
|
||||
|
||||
def show(p):
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ If you install one of these interpreters only with `--name` option, installer wi
|
|||
```
|
||||
|
||||
#### Install Spark interpreter built with Scala 2.10
|
||||
Spark distribution package has been built with Scala 2.10 until 1.6.2. If you have `SPARK_HOME` set pointing to Spark version ealier than 2.0.0, you need to download Spark interpreter packaged with Scala 2.10. To do so, use follow command:
|
||||
Spark distribution package has been built with Scala 2.10 until 1.6.2. If you have `SPARK_HOME` set pointing to Spark version earlier than 2.0.0, you need to download Spark interpreter packaged with Scala 2.10. To do so, use follow command:
|
||||
|
||||
```
|
||||
rm -rf ./interpreter/spark
|
||||
|
|
|
|||
|
|
@ -79,6 +79,52 @@ interpreter.start()
|
|||
|
||||
```
|
||||
|
||||
The above code will start interpreter thread inside your process. Once the interpreter is started you can configure zeppelin to connect to RemoteInterpreter by checking **Connect to existing process** checkbox and then provide **Host** and **Port** on which interpreter porocess is listening as shown in the image below:
|
||||
The above code will start interpreter thread inside your process. Once the interpreter is started you can configure zeppelin to connect to RemoteInterpreter by checking **Connect to existing process** checkbox and then provide **Host** and **Port** on which interpreter process is listening as shown in the image below:
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/screenshots/existing_interpreter.png" width="450px">
|
||||
|
||||
|
||||
## (Experimental) Interpreter Execution Hooks
|
||||
|
||||
Zeppelin allows for users to specify additional code to be executed by an interpreter at pre and post-paragraph code execution. This is primarily useful if you need to run the same set of code for all of the paragraphs within your notebook at specific times. Currently, this feature is only available for the spark and pyspark interpreters. To specify your hook code, you may use '`z.registerHook()`. For example, enter the following into one paragraph:
|
||||
|
||||
```python
|
||||
%pyspark
|
||||
z.registerHook("post_exec", "print 'This code should be executed before the parapgraph code!'")
|
||||
z.registerHook("pre_exec", "print 'This code should be executed after the paragraph code!'")
|
||||
```
|
||||
|
||||
These calls will not take into effect until the next time you run a paragraph. In another paragraph, enter
|
||||
```python
|
||||
%pyspark
|
||||
print "This code should be entered into the paragraph by the user!"
|
||||
```
|
||||
|
||||
The output should be:
|
||||
```
|
||||
This code should be executed before the paragraph code!
|
||||
This code should be entered into the paragraph by the user!
|
||||
This code should be executed after the paragraph code!
|
||||
```
|
||||
|
||||
If you ever need to know the hook code, use `z.getHook()`:
|
||||
```python
|
||||
%pyspark
|
||||
print z.getHook("post_exec")
|
||||
```
|
||||
```
|
||||
print 'This code should be executed after the paragraph code!'
|
||||
```
|
||||
Any call to `z.registerHook()` will automatically overwrite what was previously registered. To completely unregister a hook event, use `z.unregisterHook(eventCode)`. Currently only `"post_exec"` and `"pre_exec"` are valid event codes for the Zeppelin Hook Registry system.
|
||||
|
||||
Finally, the hook registry is internally shared by other interpreters in the same group. This would allow for hook code for one interpreter REPL to be set by another as follows:
|
||||
|
||||
```scala
|
||||
%spark
|
||||
z.unregisterHook("post_exec", "pyspark")
|
||||
```
|
||||
The API is identical for both the spark (scala) and pyspark (python) implementations.
|
||||
|
||||
### Caveats
|
||||
Calls to `z.registerHook("pre_exec", ...)` should be made with care. If there are errors in your specified hook code, this will cause the interpreter REPL to become unable to execute any code pass the pre-execute stage making it impossible for direct calls to `z.unregisterHook()` to take into effect. Current workarounds include calling `z.unregisterHook()` from a different interpreter REPL in the same interpreter group (see above) or manually restarting the interpreter group in the UI.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Customize Apache Zeppelin homepage"
|
||||
description: "Apache Zeppelin allows you to use one of the notebooks you create as your Zeppelin Homepage. With that you can brand your Zeppelin installation, adjust the instruction to your users needs and even translate to other languages."
|
||||
description: "Apache Zeppelin allows you to use one of the notes you create as your Zeppelin Homepage. With that you can brand your Zeppelin installation, adjust the instruction to your users needs and even translate to other languages."
|
||||
group: manual
|
||||
---
|
||||
<!--
|
||||
|
|
@ -23,53 +23,53 @@ limitations under the License.
|
|||
|
||||
<div id="toc"></div>
|
||||
|
||||
Apache Zeppelin allows you to use one of the notebooks you create as your Zeppelin Homepage.
|
||||
Apache Zeppelin allows you to use one of the notes you create as your Zeppelin Homepage.
|
||||
With that you can brand your Zeppelin installation, adjust the instruction to your users needs and even translate to other languages.
|
||||
|
||||
## How to set a notebook as your Zeppelin homepage
|
||||
## How to set a note as your Zeppelin homepage
|
||||
|
||||
The process for creating your homepage is very simple as shown below:
|
||||
|
||||
1. Create a notebook using Zeppelin
|
||||
2. Set the notebook id in the config file
|
||||
1. Create a note using Zeppelin
|
||||
2. Set the note id in the config file
|
||||
3. Restart Zeppelin
|
||||
|
||||
### Create a notebook using Zeppelin
|
||||
Create a new notebook using Zeppelin,
|
||||
### Create a note using Zeppelin
|
||||
Create a new note using Zeppelin,
|
||||
you can use ```%md``` interpreter for markdown content or any other interpreter you like.
|
||||
You can also use the display system to generate [text](../displaysystem/basicdisplaysystem.html#text), [html](../displaysystem/basicdisplaysystem.html#html), [table](../displaysystem/basicdisplaysystem.html#table) or
|
||||
Angular ([backend API](../displaysystem/back-end-angular.html), [frontend API](../displaysystem/front-end-angular.html)).
|
||||
|
||||
Run (shift+Enter) the notebook and see the output. Optionally, change the notebook view to report to hide
|
||||
Run (shift+Enter) the note and see the output. Optionally, change the note view to report to hide
|
||||
the code sections.
|
||||
|
||||
### Set the notebook id in the config file
|
||||
To set the notebook id in the config file, you should copy it from the last word in the notebook url.
|
||||
### Set the note id in the config file
|
||||
To set the note id in the config file, you should copy it from the last word in the note url.
|
||||
For example,
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/homepage_notebook_id.png" width="400px" />
|
||||
|
||||
Set the notebook id to the ```ZEPPELIN_NOTEBOOK_HOMESCREEN``` environment variable
|
||||
Set the note id to the ```ZEPPELIN_NOTEBOOK_HOMESCREEN``` environment variable
|
||||
or ```zeppelin.notebook.homescreen``` property.
|
||||
|
||||
You can also set the ```ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE``` environment variable
|
||||
or ```zeppelin.notebook.homescreen.hide``` property to hide the new notebook from the notebook list.
|
||||
or ```zeppelin.notebook.homescreen.hide``` property to hide the new note from the note list.
|
||||
|
||||
### Restart Zeppelin
|
||||
Restart your Zeppelin server
|
||||
|
||||
```
|
||||
./bin/zeppelin-deamon stop
|
||||
./bin/zeppelin-deamon start
|
||||
./bin/zeppelin-daemon stop
|
||||
./bin/zeppelin-daemon start
|
||||
```
|
||||
That's it! Open your browser and navigate to Apache Zeppelin and see your customized homepage.
|
||||
|
||||
<br />
|
||||
## Show notebooks list in your custom homepage
|
||||
If you want to display the list of notebooks on your custom Apache Zeppelin homepage all
|
||||
## Show notes list in your custom homepage
|
||||
If you want to display the list of notes on your custom Apache Zeppelin homepage all
|
||||
you need to do is use our %angular support.
|
||||
|
||||
Add the following code to a paragraph in you home page and run it... walla! you have your notebooks list.
|
||||
Add the following code to a paragraph in you home page and run it... Voila! You have your notes list.
|
||||
|
||||
```javascript
|
||||
println(
|
||||
|
|
@ -89,7 +89,7 @@ println(
|
|||
""")
|
||||
```
|
||||
|
||||
After running the notebook you will see output similar to this one:
|
||||
After running the note you will see output similar to this one:
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/homepage_notebook_list.png" />
|
||||
|
||||
The main trick here relays in linking the ```<div>``` to the controller:
|
||||
|
|
@ -99,4 +99,4 @@ The main trick here relays in linking the ```<div>``` to the controller:
|
|||
```
|
||||
|
||||
Once we have ```home``` as our controller variable in our ```<div></div>```
|
||||
we can use ```home.notes.list``` to get access to the notebook list.
|
||||
we can use ```home.notes.list``` to get access to the note list.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
-->
|
||||
|
||||
{% include JB/setup %}
|
||||
|
||||
# Install with flink and spark cluster
|
||||
|
||||
<div id="toc"></div>
|
||||
|
||||
This tutorial is extremely entry-level. It assumes no prior knowledge of Linux, git, or other tools. If you carefully type what I tell you when I tell you, you should be able to get Zeppelin running.
|
||||
|
||||
## Installing Zeppelin with Flink and Spark in cluster mode
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
|
||||
Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run, Export, Import as detailed in the following tables.
|
||||
|
||||
### List of the notebooks
|
||||
### List of the notes
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method lists the available notebooks on your server.
|
||||
Notebook JSON contains the ```name``` and ```id``` of all notebooks.
|
||||
<td>This ```GET``` method lists the available notes on your server.
|
||||
Notebook JSON contains the ```name``` and ```id``` of all notes.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -77,13 +77,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br/>
|
||||
### Create a new notebook
|
||||
### Create a new note
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method creates a new notebook using the given name or default name if none given.
|
||||
The body field of the returned JSON contains the new notebook id.
|
||||
<td>This ```POST``` method creates a new note using the given name or default name if none given.
|
||||
The body field of the returned JSON contains the new note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -100,13 +100,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON input (without paragraphs) </td>
|
||||
<td><pre>{"name": "name of new notebook"}</pre></td>
|
||||
<td><pre>{"name": "name of new note"}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON input (with initial paragraphs) </td>
|
||||
<td><pre>
|
||||
{
|
||||
"name": "name of new notebook",
|
||||
"name": "name of new note",
|
||||
"paragraphs": [
|
||||
{
|
||||
"title": "paragraph title1",
|
||||
|
|
@ -131,18 +131,18 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br/>
|
||||
### Get an existing notebook information
|
||||
### Get an existing note information
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method retrieves an existing notebook's information using the given id.
|
||||
The body field of the returned JSON contain information about paragraphs in the notebook.
|
||||
<td>This ```GET``` method retrieves an existing note's information using the given id.
|
||||
The body field of the returned JSON contain information about paragraphs in the note.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -228,17 +228,17 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br/>
|
||||
### Delete a notebook
|
||||
### Delete a note
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method deletes a notebook by the given notebook id.
|
||||
<td>This ```DELETE``` method deletes a note by the given note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -255,19 +255,19 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br/>
|
||||
### Clone a notebook
|
||||
### Clone a note
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method clones a notebook by the given id and create a new notebook using the given name
|
||||
<td>This ```POST``` method clones a note by the given id and create a new note using the given name
|
||||
or default name if none given.
|
||||
The body field of the returned JSON contains the new notebook id.
|
||||
The body field of the returned JSON contains the new note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -279,7 +279,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON input </td>
|
||||
<td><pre>{"name": "name of new notebook"}</pre></td>
|
||||
<td><pre>{"name": "name of new note"}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON response </td>
|
||||
|
|
@ -299,14 +299,14 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<tr>
|
||||
<td>Description</td>
|
||||
<td>
|
||||
This ```POST``` method runs all paragraphs in the given notebook id. <br />
|
||||
If you can not find Notebook id 404 returns.
|
||||
This ```POST``` method runs all paragraphs in the given note id. <br />
|
||||
If you can not find Note id 404 returns.
|
||||
If there is a problem with the interpreter returns a 412 error.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -345,12 +345,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method stops all paragraphs in the given notebook id.
|
||||
<td>This ```DELETE``` method stops all paragraphs in the given note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -372,13 +372,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method gets the status of all paragraphs by the given notebook id.
|
||||
<td>This ```GET``` method gets the status of all paragraphs by the given note id.
|
||||
The body field of the returned JSON contains of the array that compose of the paragraph id, paragraph status, paragraph finish date, paragraph started date.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -418,13 +418,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method gets the status of a single paragraph by the given notebook and paragraph id.
|
||||
<td>This ```GET``` method gets the status of a single paragraph by the given note and paragraph id.
|
||||
The body field of the returned JSON contains of the array that compose of the paragraph id, paragraph status, paragraph finish date, paragraph started date.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -455,12 +455,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>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
|
||||
<td>This ```POST``` method runs the paragraph asynchronously by given note and paragraph id. This API always return SUCCESS even if the execution of the paragraph fails later because the API is asynchronous
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -474,7 +474,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<td> sample JSON input (optional, only needed when if you want to update dynamic form's value) </td>
|
||||
<td><pre>
|
||||
{
|
||||
"name": "name of new notebook",
|
||||
"name": "name of new note",
|
||||
"params": {
|
||||
"formLabel1": "value1",
|
||||
"formLabel2": "value2"
|
||||
|
|
@ -493,12 +493,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td> 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
|
||||
<td> This ```POST``` method runs the paragraph synchronously by given note and paragraph id. This API can return SUCCESS or ERROR depending on the outcome of the paragraph execution
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/run/[noteId]/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -512,7 +512,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<td> sample JSON input (optional, only needed when if you want to update dynamic form's value) </td>
|
||||
<td><pre>
|
||||
{
|
||||
"name": "name of new notebook",
|
||||
"name": "name of new note",
|
||||
"params": {
|
||||
"formLabel1": "value1",
|
||||
"formLabel2": "value2"
|
||||
|
|
@ -543,12 +543,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method stops the paragraph by given notebook and paragraph id.
|
||||
<td>This ```DELETE``` method stops the paragraph by given note and paragraph id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[noteId]/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -570,12 +570,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method adds cron job by the given notebook id.
|
||||
<td>This ```POST``` method adds cron job by the given note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -587,7 +587,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON input </td>
|
||||
<td><pre>{"cron": "cron expression of notebook"}</pre></td>
|
||||
<td><pre>{"cron": "cron expression of note"}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON response </td>
|
||||
|
|
@ -602,12 +602,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method removes cron job by the given notebook id.
|
||||
<td>This ```DELETE``` method removes cron job by the given note id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -630,13 +630,13 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method gets cron job expression of given notebook id.
|
||||
<td>This ```GET``` method gets cron job expression of given note id.
|
||||
The body field of the returned JSON contains the cron expression.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[notebookId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/cron/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -653,7 +653,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br />
|
||||
### Full text search through the paragraphs in all notebooks
|
||||
### Full text search through the paragraphs in all notes
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
|
|
@ -681,7 +681,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
"body": [
|
||||
{
|
||||
"id": "<noteId>/paragraph/<paragraphId>",
|
||||
"name":"Notebook Name",
|
||||
"name":"Note Name",
|
||||
"snippet":"",
|
||||
"text":""
|
||||
}
|
||||
|
|
@ -702,7 +702,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]/paragraph```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]/paragraph```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -752,7 +752,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]/paragraph/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]/paragraph/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -814,12 +814,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method moves a paragraph to the specific index (order) from the notebook.
|
||||
<td>This ```POST``` method moves a paragraph to the specific index (order) from the note.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]/paragraph/[paragraphId]/move/[newIndex]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]/paragraph/[paragraphId]/move/[newIndex]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -842,12 +842,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method deletes a paragraph by the given notebook and paragraph id.
|
||||
<td>This ```DELETE``` method deletes a paragraph by the given note and paragraph id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[notebookId]/paragraph/[paragraphId]```</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/[noteId]/paragraph/[paragraphId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -864,17 +864,17 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br />
|
||||
### Export a notebook
|
||||
### Export a note
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method exports a notebook by the given id and gernerates a JSON
|
||||
<td>This ```GET``` method exports a note 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>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/export/[noteId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
|
|
@ -914,12 +914,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
</table>
|
||||
|
||||
<br />
|
||||
### Import a notebook
|
||||
### Import a note
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method imports a notebook from the notebook JSON input
|
||||
<td>This ```POST``` method imports a note from the note JSON input
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.net.InetAddress;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
|
@ -35,7 +36,6 @@ import java.util.regex.Pattern;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
|
|
@ -48,6 +48,8 @@ import org.elasticsearch.client.Client;
|
|||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
|
|
@ -437,14 +439,37 @@ public class ElasticsearchInterpreter extends Interpreter {
|
|||
resMsg = XContentHelper.toString((InternalSingleBucketAggregation) agg).toString();
|
||||
}
|
||||
else if (agg instanceof InternalMultiBucketAggregation) {
|
||||
final StringBuffer buffer = new StringBuffer("key\tdoc_count");
|
||||
|
||||
final Set<String> headerKeys = new HashSet<>();
|
||||
final List<Map<String, Object>> buckets = new LinkedList<>();
|
||||
final InternalMultiBucketAggregation multiBucketAgg = (InternalMultiBucketAggregation) agg;
|
||||
|
||||
for (MultiBucketsAggregation.Bucket bucket : multiBucketAgg.getBuckets()) {
|
||||
buffer.append("\n")
|
||||
.append(bucket.getKeyAsString())
|
||||
.append("\t")
|
||||
.append(bucket.getDocCount());
|
||||
try {
|
||||
final XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
bucket.toXContent(builder, null);
|
||||
final Map<String, Object> bucketMap = JsonFlattener.flattenAsMap(builder.string());
|
||||
headerKeys.addAll(bucketMap.keySet());
|
||||
buckets.add(bucketMap);
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Processing bucket: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
final String[] keys = headerKeys.toArray(new String[0]);
|
||||
for (String key: keys) {
|
||||
buffer.append("\t" + key);
|
||||
}
|
||||
buffer.deleteCharAt(0);
|
||||
|
||||
for (Map<String, Object> bucket : buckets) {
|
||||
buffer.append("\n");
|
||||
|
||||
for (String key: keys) {
|
||||
buffer.append(bucket.get(key)).append("\t");
|
||||
}
|
||||
buffer.deleteCharAt(buffer.length() - 1);
|
||||
}
|
||||
|
||||
resType = InterpreterResult.Type.TABLE;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.math.RandomUtils;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
|
|
@ -178,6 +183,11 @@ public class ElasticsearchInterpreterTest {
|
|||
res = interpreter.interpret("search /logs { \"aggs\" : { \"status_count\" : " +
|
||||
" { \"terms\" : { \"field\" : \"status\" } } } }", null);
|
||||
assertEquals(Code.SUCCESS, res.code());
|
||||
|
||||
res = interpreter.interpret("search /logs { \"aggs\" : { " +
|
||||
" \"length\" : { \"terms\": { \"field\": \"status\" }, " +
|
||||
" \"aggs\" : { \"sum_length\" : { \"sum\" : { \"field\" : \"content_length\" } }, \"sum_status\" : { \"sum\" : { \"field\" : \"status\" } } } } } }", null);
|
||||
assertEquals(Code.SUCCESS, res.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import java.util.Properties;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
|
|
@ -38,7 +37,7 @@ import com.gemstone.gemfire.cache.query.Struct;
|
|||
import com.gemstone.gemfire.pdx.PdxInstance;
|
||||
|
||||
/**
|
||||
* Apache Geode OQL Interpreter (http://geode.incubator.apache.org)
|
||||
* Apache Geode OQL Interpreter (http://geode.apache.org)
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code geode.locator.host} - The Geode Locator {@code <HOST>} to connect to.</li>
|
||||
|
|
@ -87,30 +86,12 @@ public class GeodeOqlInterpreter extends Interpreter {
|
|||
|
||||
private Logger logger = LoggerFactory.getLogger(GeodeOqlInterpreter.class);
|
||||
|
||||
public static final String DEFAULT_PORT = "10334";
|
||||
public static final String DEFAULT_HOST = "localhost";
|
||||
public static final String DEFAULT_MAX_RESULT = "1000";
|
||||
|
||||
private static final char NEWLINE = '\n';
|
||||
private static final char TAB = '\t';
|
||||
private static final char WHITESPACE = ' ';
|
||||
|
||||
private static final String TABLE_MAGIC_TAG = "%table ";
|
||||
|
||||
public static final String LOCATOR_HOST = "geode.locator.host";
|
||||
public static final String LOCATOR_PORT = "geode.locator.port";
|
||||
public static final String MAX_RESULT = "geode.max.result";
|
||||
|
||||
static {
|
||||
Interpreter.register(
|
||||
"oql",
|
||||
"geode",
|
||||
GeodeOqlInterpreter.class.getName(),
|
||||
new InterpreterPropertyBuilder().add(LOCATOR_HOST, DEFAULT_HOST, "The Geode Locator Host.")
|
||||
.add(LOCATOR_PORT, DEFAULT_PORT, "The Geode Locator Port")
|
||||
.add(MAX_RESULT, DEFAULT_MAX_RESULT, "Max number of OQL result to display.").build());
|
||||
}
|
||||
|
||||
private ClientCache clientCache = null;
|
||||
private QueryService queryService = null;
|
||||
private Exception exceptionOnConnect;
|
||||
|
|
@ -122,8 +103,8 @@ public class GeodeOqlInterpreter extends Interpreter {
|
|||
|
||||
protected ClientCache getClientCache() {
|
||||
|
||||
String locatorHost = getProperty(LOCATOR_HOST);
|
||||
int locatorPort = Integer.valueOf(getProperty(LOCATOR_PORT));
|
||||
String locatorHost = getProperty("geode.locator.host");
|
||||
int locatorPort = Integer.valueOf(getProperty("geode.locator.port"));
|
||||
|
||||
ClientCache clientCache =
|
||||
new ClientCacheFactory().addPoolLocator(locatorHost, locatorPort).create();
|
||||
|
|
@ -139,7 +120,7 @@ public class GeodeOqlInterpreter extends Interpreter {
|
|||
close();
|
||||
|
||||
try {
|
||||
maxResult = Integer.valueOf(getProperty(MAX_RESULT));
|
||||
maxResult = Integer.valueOf(getProperty("geode.max.result"));
|
||||
|
||||
clientCache = getClientCache();
|
||||
queryService = clientCache.getQueryService();
|
||||
|
|
|
|||
30
geode/src/main/resources/interpreter-setting.json
Normal file
30
geode/src/main/resources/interpreter-setting.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"group": "geode",
|
||||
"name": "oql",
|
||||
"className": "org.apache.zeppelin.geode.GeodeOqlInterpreter",
|
||||
"properties": {
|
||||
"geode.locator.host": {
|
||||
"envName": null,
|
||||
"propertyName": "geode.locator.host",
|
||||
"defaultValue": "localhost",
|
||||
"description": "The Geode Locator Host."
|
||||
},
|
||||
"geode.locator.port": {
|
||||
"envName": null,
|
||||
"propertyName": "geode.locator.port",
|
||||
"defaultValue": "10334",
|
||||
"description": "The Geode Locator Port."
|
||||
},
|
||||
"geode.max.result": {
|
||||
"envName": null,
|
||||
"propertyName": "geode.max.result",
|
||||
"defaultValue": "1000",
|
||||
"description": "Max number of OQL result to display."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -58,9 +58,9 @@ public class GeodeOqlInterpreterTest {
|
|||
public void testOpenCommandIndempotency() {
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put(LOCATOR_HOST, DEFAULT_HOST);
|
||||
properties.put(LOCATOR_PORT, DEFAULT_PORT);
|
||||
properties.put(MAX_RESULT, DEFAULT_MAX_RESULT);
|
||||
properties.put("geode.locator.host", "localhost");
|
||||
properties.put("geode.locator.port", "10334");
|
||||
properties.put("geode.max.result", "1000");
|
||||
|
||||
GeodeOqlInterpreter spyGeodeOqlInterpreter = spy(new GeodeOqlInterpreter(properties));
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public class JDBCInterpreter extends Interpreter {
|
|||
|
||||
private Logger logger = LoggerFactory.getLogger(JDBCInterpreter.class);
|
||||
|
||||
static final String INTERPRETER_NAME = "jdbc";
|
||||
static final String COMMON_KEY = "common";
|
||||
static final String MAX_LINE_KEY = "max_count";
|
||||
static final String MAX_LINE_DEFAULT = "1000";
|
||||
|
|
@ -343,9 +344,17 @@ public class JDBCInterpreter extends Interpreter {
|
|||
return null;
|
||||
}
|
||||
|
||||
private String getEntityName(String replName) {
|
||||
StringBuffer entityName = new StringBuffer();
|
||||
entityName.append(INTERPRETER_NAME);
|
||||
entityName.append(".");
|
||||
entityName.append(replName);
|
||||
return entityName.toString();
|
||||
}
|
||||
|
||||
public void setAccountOfCredential(String propertyKey, InterpreterContext interpreterContext) {
|
||||
UsernamePassword usernamePassword = getUsernamePassword(interpreterContext,
|
||||
interpreterContext.getReplName());
|
||||
getEntityName(interpreterContext.getReplName()));
|
||||
if (usernamePassword != null && notExistAccountInProperty()) {
|
||||
propertiesMap.get(propertyKey).setProperty("user", usernamePassword.getUsername());
|
||||
propertiesMap.get(propertyKey).setProperty("password", usernamePassword.getPassword());
|
||||
|
|
@ -452,6 +461,14 @@ public class JDBCInterpreter extends Interpreter {
|
|||
PrintStream ps = new PrintStream(baos);
|
||||
e.printStackTrace(ps);
|
||||
String errorMsg = new String(baos.toByteArray(), StandardCharsets.UTF_8);
|
||||
|
||||
try {
|
||||
PoolingDriver driver = poolingDriverMap.remove(propertyKey);
|
||||
driver.closePool(propertyKey);
|
||||
} catch (SQLException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
return new InterpreterResult(Code.ERROR, errorMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,24 +56,6 @@ public class KylinInterpreter extends Interpreter {
|
|||
static final Pattern KYLIN_TABLE_FORMAT_REGEX_LABEL = Pattern.compile("\"label\":\"(.*?)\"");
|
||||
static final Pattern KYLIN_TABLE_FORMAT_REGEX = Pattern.compile("\"results\":\\[\\[\"(.*?)\"]]");
|
||||
|
||||
static {
|
||||
Interpreter.register(
|
||||
"kylin",
|
||||
"kylin",
|
||||
KylinInterpreter.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add(KYLIN_USERNAME, "ADMIN", "username for kylin user")
|
||||
.add(KYLIN_PASSWORD, "KYLIN", "password for kylin user")
|
||||
.add(KYLIN_QUERY_API_URL, "http://<host>:<port>/kylin/api/query", "Kylin API.")
|
||||
.add(KYLIN_QUERY_PROJECT, "default", "kylin project name")
|
||||
.add(KYLIN_QUERY_OFFSET, "0", "kylin query offset")
|
||||
.add(KYLIN_QUERY_LIMIT, "5000", "kylin query limit")
|
||||
.add(KYLIN_QUERY_ACCEPT_PARTIAL, "true", "The kylin query partial flag").build());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public KylinInterpreter(Properties property) {
|
||||
super(property);
|
||||
}
|
||||
|
|
|
|||
54
kylin/src/main/resources/interpreter-setting.json
Normal file
54
kylin/src/main/resources/interpreter-setting.json
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
[
|
||||
{
|
||||
"group": "kylin",
|
||||
"name": "kylin",
|
||||
"className": "org.apache.zeppelin.kylin.KylinInterpreter",
|
||||
"properties": {
|
||||
"kylin.api.url": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.api.url",
|
||||
"defaultValue": "http://localhost:7070/kylin/api/query",
|
||||
"description": "Kylin API"
|
||||
},
|
||||
"kylin.api.user": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.api.user",
|
||||
"defaultValue": "ADMIN",
|
||||
"description": "username for kylin user"
|
||||
},
|
||||
"kylin.api.password": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.api.password",
|
||||
"defaultValue": "KYLIN",
|
||||
"description": "password for kylin user"
|
||||
},
|
||||
"kylin.query.project": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.query.project",
|
||||
"defaultValue": "default",
|
||||
"description": "kylin project name"
|
||||
},
|
||||
"kylin.query.offset": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.query.offset",
|
||||
"defaultValue": "0",
|
||||
"description": "kylin query offset"
|
||||
},
|
||||
"kylin.query.limit": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.query.limit",
|
||||
"defaultValue": "5000",
|
||||
"description": "kylin query limit"
|
||||
},
|
||||
"kylin.query.ispartial": {
|
||||
"envName": null,
|
||||
"propertyName": "kylin.query.ispartial",
|
||||
"defaultValue": "true",
|
||||
"description": "The kylin query partial flag"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -20,8 +20,7 @@ import org.apache.http.client.methods.HttpPost;
|
|||
import org.apache.http.message.AbstractHttpMessage;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.kylin.KylinInterpreter;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
|
@ -33,19 +32,23 @@ import java.util.Properties;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
public class KylinInterpreterTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
static final Properties kylinProperties = new Properties();
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
kylinProperties.put("kylin.api.url", "http://localhost:7070/kylin/api/query");
|
||||
kylinProperties.put("kylin.api.user", "ADMIN");
|
||||
kylinProperties.put("kylin.api.password", "KYLIN");
|
||||
kylinProperties.put("kylin.query.project", "default");
|
||||
kylinProperties.put("kylin.query.offset", "0");
|
||||
kylinProperties.put("kylin.query.limit", "5000");
|
||||
kylinProperties.put("kylin.query.ispartial", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
KylinInterpreter t = new MockKylinInterpreter(new Properties());
|
||||
KylinInterpreter t = new MockKylinInterpreter(kylinProperties);
|
||||
InterpreterResult result = t.interpret(
|
||||
"select a.date,sum(b.measure) as measure from kylin_fact_table a " +
|
||||
"inner join kylin_lookup_table b on a.date=b.date group by a.date", null);
|
||||
|
|
@ -198,4 +201,4 @@ class MockEntity implements HttpEntity{
|
|||
public void consumeContent() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public class LensInterpreter extends Interpreter {
|
|||
|
||||
private static Pattern s_queryExecutePattern = Pattern.compile(".*query\\s+execute\\s+(.*)");
|
||||
private static Map<String, ExecutionDetail> s_paraToQH =
|
||||
new ConcurrentHashMap<String, ExecutionDetail> (); //tracks paragraphID -> Lens QueryHandle
|
||||
new ConcurrentHashMap<String, ExecutionDetail> (); //tracks paragraphId -> Lens QueryHandle
|
||||
private static Map<LensClient, Boolean> s_clientMap =
|
||||
new ConcurrentHashMap<LensClient, Boolean>();
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class PythonInterpreter extends Interpreter {
|
|||
|
||||
@Override
|
||||
public void open() {
|
||||
LOG.info("Starting Python interpreter .....");
|
||||
LOG.info("Starting Python interpreter ---->");
|
||||
LOG.info("Python path is set to:" + property.getProperty(ZEPPELIN_PYTHON));
|
||||
|
||||
maxResult = Integer.valueOf(getProperty(MAX_RESULT));
|
||||
|
|
@ -111,7 +111,7 @@ public class PythonInterpreter extends Interpreter {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
LOG.info("closing Python interpreter .....");
|
||||
LOG.info("closing Python interpreter <----");
|
||||
try {
|
||||
if (process != null) {
|
||||
process.close();
|
||||
|
|
@ -134,11 +134,9 @@ public class PythonInterpreter extends Interpreter {
|
|||
|
||||
InterpreterResult result;
|
||||
if (pythonErrorIn(output)) {
|
||||
result = new InterpreterResult(Code.ERROR, output);
|
||||
result = new InterpreterResult(Code.ERROR, output.replaceAll("\\.\\.\\.", ""));
|
||||
} else {
|
||||
// TODO(zjffdu), we should not do string replacement operation in the result, as it is
|
||||
// possible that the output contains the kind of pattern itself, e.g. print("...")
|
||||
result = new InterpreterResult(Code.SUCCESS, output.replaceAll("\\.\\.\\.", ""));
|
||||
result = new InterpreterResult(Code.SUCCESS, output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,6 @@ public class PythonProcess {
|
|||
String line = null;
|
||||
while (!(line = reader.readLine()).contains(STATEMENT_END)) {
|
||||
logger.debug("Read line from python shell : " + line);
|
||||
if (line.equals("...")) {
|
||||
logger.warn("Syntax error ! ");
|
||||
output.append("Syntax error ! ");
|
||||
break;
|
||||
}
|
||||
output.append(line + "\n");
|
||||
}
|
||||
return output.toString();
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ public class PythonInterpreterWithPythonInstalledTest {
|
|||
//System.out.println("\nInterpreter response: \n" + ret.message());
|
||||
assertEquals(InterpreterResult.Code.ERROR, ret.code());
|
||||
assertTrue(ret.message().length() > 0);
|
||||
|
||||
realPython.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -73,6 +75,36 @@ public class PythonInterpreterWithPythonInstalledTest {
|
|||
//System.out.println("\nInterpreter response: \n" + ret.message());
|
||||
assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
|
||||
assertTrue(ret.message().length() > 0);
|
||||
|
||||
realPython.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZeppelin1555() {
|
||||
//given
|
||||
PythonInterpreter realPython = new PythonInterpreter(
|
||||
PythonInterpreterTest.getPythonTestProperties());
|
||||
realPython.open();
|
||||
|
||||
//when
|
||||
InterpreterResult ret1 = realPython.interpret("print \"...\"", null);
|
||||
|
||||
//then
|
||||
//System.out.println("\nInterpreter response: \n" + ret.message());
|
||||
assertEquals(InterpreterResult.Code.SUCCESS, ret1.code());
|
||||
assertEquals("...\n", ret1.message());
|
||||
|
||||
|
||||
InterpreterResult ret2 = realPython.interpret("for i in range(5):", null);
|
||||
//then
|
||||
//System.out.println("\nInterpreterResultterpreter response: \n" + ret2.message());
|
||||
assertEquals(InterpreterResult.Code.ERROR, ret2.code());
|
||||
assertEquals(" File \"<stdin>\", line 2\n" +
|
||||
" \n" +
|
||||
" ^\n" +
|
||||
"IndentationError: expected an indented block\n", ret2.message());
|
||||
|
||||
realPython.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ import matplotlib.pyplot as plt
|
|||
import numpy as np
|
||||
import StringIO
|
||||
|
||||
# clear out any previous plots on this notebook
|
||||
# clear out any previous plots on this note
|
||||
plt.clf()
|
||||
|
||||
def show(p):
|
||||
|
|
|
|||
|
|
@ -525,9 +525,9 @@
|
|||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<spark.version>2.0.0</spark.version>
|
||||
<spark.version>2.0.1</spark.version>
|
||||
<protobuf.version>2.5.0</protobuf.version>
|
||||
<py4j.version>0.10.1</py4j.version>
|
||||
<py4j.version>0.10.3</py4j.version>
|
||||
<scala.version>2.11.8</scala.version>
|
||||
</properties>
|
||||
</profile>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<jsoup.version>1.8.2</jsoup.version>
|
||||
<mockito.version>1.10.19</mockito.version>
|
||||
<powermock.version>1.6.4</powermock.version>
|
||||
<spark.version>2.0.0</spark.version>
|
||||
<spark.version>2.0.1</spark.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -519,9 +519,9 @@
|
|||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<spark.version>2.0.0</spark.version>
|
||||
<spark.version>2.0.1</spark.version>
|
||||
<protobuf.version>2.5.0</protobuf.version>
|
||||
<py4j.version>0.10.1</py4j.version>
|
||||
<py4j.version>0.10.3</py4j.version>
|
||||
<scala.version>2.11.8</scala.version>
|
||||
</properties>
|
||||
</profile>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import org.apache.spark.ui.jobs.JobProgressListener;
|
|||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
import org.apache.zeppelin.interpreter.InterpreterHookRegistry;
|
||||
import org.apache.zeppelin.interpreter.InterpreterProperty;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
|
|
@ -101,6 +102,7 @@ public class SparkInterpreter extends Interpreter {
|
|||
private SparkConf conf;
|
||||
private static SparkContext sc;
|
||||
private static SQLContext sqlc;
|
||||
private static InterpreterHookRegistry hooks;
|
||||
private static SparkEnv env;
|
||||
private static Object sparkSession; // spark 2.x
|
||||
private static JobProgressListener sparkListener;
|
||||
|
|
@ -479,7 +481,7 @@ public class SparkInterpreter extends Interpreter {
|
|||
|
||||
//Only one of py4j-0.9-src.zip and py4j-0.8.2.1-src.zip should exist
|
||||
String[] pythonLibs = new String[]{"pyspark.zip", "py4j-0.9-src.zip", "py4j-0.8.2.1-src.zip",
|
||||
"py4j-0.10.1-src.zip"};
|
||||
"py4j-0.10.1-src.zip", "py4j-0.10.3-src.zip"};
|
||||
ArrayList<String> pythonLibUris = new ArrayList<>();
|
||||
for (String lib : pythonLibs) {
|
||||
File libFile = new File(pysparkPath, lib);
|
||||
|
|
@ -813,8 +815,10 @@ public class SparkInterpreter extends Interpreter {
|
|||
sqlc = getSQLContext();
|
||||
|
||||
dep = getDependencyResolver();
|
||||
|
||||
hooks = getInterpreterGroup().getInterpreterHookRegistry();
|
||||
|
||||
z = new ZeppelinContext(sc, sqlc, null, dep,
|
||||
z = new ZeppelinContext(sc, sqlc, null, dep, hooks,
|
||||
Integer.parseInt(getProperty("zeppelin.spark.maxResult")));
|
||||
|
||||
interpret("@transient val _binder = new java.util.HashMap[String, Object]()");
|
||||
|
|
|
|||
|
|
@ -28,11 +28,14 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.spark.SparkContext;
|
||||
import org.apache.spark.sql.SQLContext;
|
||||
import org.apache.spark.sql.catalyst.expressions.Attribute;
|
||||
import org.apache.zeppelin.annotation.ZeppelinApi;
|
||||
import org.apache.zeppelin.annotation.Experimental;
|
||||
import org.apache.zeppelin.display.AngularObject;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObjectWatcher;
|
||||
|
|
@ -41,6 +44,7 @@ import org.apache.zeppelin.display.Input.ParamOption;
|
|||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContextRunner;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
import org.apache.zeppelin.interpreter.InterpreterHookRegistry;
|
||||
import org.apache.zeppelin.spark.dep.SparkDependencyResolver;
|
||||
import org.apache.zeppelin.resource.Resource;
|
||||
import org.apache.zeppelin.resource.ResourcePool;
|
||||
|
|
@ -53,19 +57,33 @@ import scala.Unit;
|
|||
* Spark context for zeppelin.
|
||||
*/
|
||||
public class ZeppelinContext {
|
||||
// Map interpreter class name (to be used by hook registry) from
|
||||
// given replName in parapgraph
|
||||
private static final Map<String, String> interpreterClassMap;
|
||||
static {
|
||||
interpreterClassMap = new HashMap<String, String>();
|
||||
interpreterClassMap.put("spark", "org.apache.zeppelin.spark.SparkInterpreter");
|
||||
interpreterClassMap.put("sql", "org.apache.zeppelin.spark.SparkSqlInterpreter");
|
||||
interpreterClassMap.put("dep", "org.apache.zeppelin.spark.DepInterpreter");
|
||||
interpreterClassMap.put("pyspark", "org.apache.zeppelin.spark.PySparkInterpreter");
|
||||
}
|
||||
|
||||
private SparkDependencyResolver dep;
|
||||
private InterpreterContext interpreterContext;
|
||||
private int maxResult;
|
||||
private List<Class> supportedClasses;
|
||||
|
||||
private InterpreterHookRegistry hooks;
|
||||
|
||||
public ZeppelinContext(SparkContext sc, SQLContext sql,
|
||||
InterpreterContext interpreterContext,
|
||||
SparkDependencyResolver dep,
|
||||
InterpreterHookRegistry hooks,
|
||||
int maxResult) {
|
||||
this.sc = sc;
|
||||
this.sqlContext = sql;
|
||||
this.interpreterContext = interpreterContext;
|
||||
this.dep = dep;
|
||||
this.hooks = hooks;
|
||||
this.maxResult = maxResult;
|
||||
this.supportedClasses = new ArrayList<>();
|
||||
try {
|
||||
|
|
@ -697,6 +715,90 @@ public class ZeppelinContext {
|
|||
registry.remove(name, noteId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interpreter class name from name entered in paragraph
|
||||
* @param replName if replName is a valid className, return that instead.
|
||||
*/
|
||||
public String getClassNameFromReplName(String replName) {
|
||||
for (String name : interpreterClassMap.values()) {
|
||||
if (replName.equals(name)) {
|
||||
return replName;
|
||||
}
|
||||
}
|
||||
|
||||
if (replName.contains("spark.")) {
|
||||
replName = replName.replace("spark.", "");
|
||||
}
|
||||
return interpreterClassMap.get(replName);
|
||||
}
|
||||
|
||||
/**
|
||||
* General function to register hook event
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param cmd The code to be executed by the interpreter on given event
|
||||
* @param replName Name of the interpreter
|
||||
*/
|
||||
@Experimental
|
||||
public void registerHook(String event, String cmd, String replName) {
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
String className = getClassNameFromReplName(replName);
|
||||
hooks.register(noteId, className, event, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* registerHook() wrapper for current repl
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param cmd The code to be executed by the interpreter on given event
|
||||
*/
|
||||
@Experimental
|
||||
public void registerHook(String event, String cmd) {
|
||||
String className = interpreterContext.getClassName();
|
||||
registerHook(event, cmd, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hook code
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param replName Name of the interpreter
|
||||
*/
|
||||
@Experimental
|
||||
public String getHook(String event, String replName) {
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
String className = getClassNameFromReplName(replName);
|
||||
return hooks.get(noteId, className, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* getHook() wrapper for current repl
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public String getHook(String event) {
|
||||
String className = interpreterContext.getClassName();
|
||||
return getHook(event, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind code from given hook event
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param replName Name of the interpreter
|
||||
*/
|
||||
@Experimental
|
||||
public void unregisterHook(String event, String replName) {
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
String className = getClassNameFromReplName(replName);
|
||||
hooks.unregister(noteId, className, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* unregisterHook() wrapper for current repl
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public void unregisterHook(String event) {
|
||||
String className = interpreterContext.getClassName();
|
||||
unregisterHook(event, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add object into resource pool
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ public class ZeppelinR implements ExecuteResultHandler {
|
|||
cmd.addArgument(Integer.toString(port));
|
||||
cmd.addArgument(libPath);
|
||||
cmd.addArgument(Integer.toString(sparkVersion.toNumber()));
|
||||
|
||||
// dump out the R command to facilitate manually running it, e.g. for fault diagnosis purposes
|
||||
logger.debug(cmd.toString());
|
||||
|
||||
executor = new DefaultExecutor();
|
||||
outputStream = new SparkOutputStream(logger);
|
||||
|
|
|
|||
|
|
@ -80,16 +80,16 @@ class PyZeppelinContext(dict):
|
|||
def get(self, key):
|
||||
return self.__getitem__(key)
|
||||
|
||||
def input(self, name, defaultValue = ""):
|
||||
def input(self, name, defaultValue=""):
|
||||
return self.z.input(name, defaultValue)
|
||||
|
||||
def select(self, name, options, defaultValue = ""):
|
||||
def select(self, name, options, defaultValue=""):
|
||||
# auto_convert to ArrayList doesn't match the method signature on JVM side
|
||||
tuples = list(map(lambda items: self.__tupleToScalaTuple2(items), options))
|
||||
iterables = gateway.jvm.scala.collection.JavaConversions.collectionAsScalaIterable(tuples)
|
||||
return self.z.select(name, defaultValue, iterables)
|
||||
|
||||
def checkbox(self, name, options, defaultChecked = None):
|
||||
def checkbox(self, name, options, defaultChecked=None):
|
||||
if defaultChecked is None:
|
||||
defaultChecked = list(map(lambda items: items[0], options))
|
||||
optionTuples = list(map(lambda items: self.__tupleToScalaTuple2(items), options))
|
||||
|
|
@ -99,6 +99,23 @@ class PyZeppelinContext(dict):
|
|||
checkedIterables = self.z.checkbox(name, defaultCheckedIterables, optionIterables)
|
||||
return gateway.jvm.scala.collection.JavaConversions.asJavaCollection(checkedIterables)
|
||||
|
||||
def registerHook(self, event, cmd, replName=None):
|
||||
if replName is None:
|
||||
self.z.registerHook(event, cmd)
|
||||
else:
|
||||
self.z.registerHook(event, cmd, replName)
|
||||
|
||||
def unregisterHook(self, event, replName=None):
|
||||
if replName is None:
|
||||
self.z.unregisterHook(event)
|
||||
else:
|
||||
self.z.unregisterHook(event, replName)
|
||||
|
||||
def getHook(self, event, replName=None):
|
||||
if replName is None:
|
||||
return self.z.getHook(event)
|
||||
return self.z.getHook(event, replName)
|
||||
|
||||
def __tupleToScalaTuple2(self, tuple):
|
||||
if (len(tuple) == 2):
|
||||
return gateway.jvm.scala.Tuple2(tuple[0], tuple[1])
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ object AngularElem {
|
|||
}
|
||||
|
||||
/**
|
||||
* Disassociate (remove) all angular object in this notebook
|
||||
* Disassociate (remove) all angular object in this note
|
||||
*/
|
||||
def disassociate() = {
|
||||
val ic = InterpreterContext.get
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ object AngularElem {
|
|||
}
|
||||
|
||||
/**
|
||||
* Disassociate (remove) all angular object in this notebook
|
||||
* Disassociate (remove) all angular object in this note
|
||||
*/
|
||||
def disassociate() = {
|
||||
val ic = InterpreterContext.get
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ The following components are provided under Apache License.
|
|||
(Apache 2.0) Utility classes for Jetty (org.mortbay.jetty:jetty-util:6.1.26 - http://javadox.com/org.mortbay.jetty/jetty/6.1.26/overview-tree.html)
|
||||
(Apache 2.0) Servlet API (org.mortbay.jetty:servlet-api:2.5-20081211 - https://en.wikipedia.org/wiki/Jetty_(web_server))
|
||||
(Apache 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client-jackson2:1.21.0 - https://github.com/google/google-http-java-client/tree/dev/google-http-client-jackson2)
|
||||
(Apache 2.0) angular-esri-map (https://github.com/Esri/angular-esri-map)
|
||||
(Apache 2.0) pegdown (org.pegdown:pegdown:1.6.0 - https://github.com/sirthias/pegdown)
|
||||
(Apache 2.0) parboiled-java (org.parboiled:parboiled-java:1.1.7 - https://github.com/sirthias/parboiled)
|
||||
(Apache 2.0) parboiled-core (org.parboiled:parboiled-core:1.1.7 - https://github.com/sirthias/parboiled)
|
||||
|
|
@ -260,7 +259,7 @@ The following components are provided under the BSD-style License.
|
|||
(BSD-like) ASM asm-utils (org.ow2.asm:asm-utils:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(New BSD License) Markdown4j (org.commonjava.googlecode.markdown4j:markdown4j:jar:2.2-cj-1.0 - https://code.google.com/p/markdown4j/)
|
||||
(New BSD License) Py4J (net.sf.py4j:py4j:0.9 - http://py4j.sourceforge.net/)
|
||||
(New BSD License) Py4J (net.sf.py4j:py4j:0.10.1 - http://py4j.sourceforge.net/) - https://github.com/bartdag/py4j/blob/0.10.1/LICENSE.txt
|
||||
(New BSD License) Py4J (net.sf.py4j:py4j:0.10.3 - http://py4j.sourceforge.net/) - https://github.com/bartdag/py4j/blob/0.10.3/LICENSE.txt
|
||||
(New BSD License) Markdown4j (org.commonjava.googlecode.markdown4j:markdown4j:jar:2.2-cj-1.0 - https://code.google.com/p/markdown4j/)
|
||||
(BSD 3 Clause) Paranamer (com.thoughtworks.paranamer:paranamer:jar:2.6) - https://github.com/paul-hammant/paranamer/blob/paranamer-parent-2.6/LICENSE.txt
|
||||
(BSD 3 Clause) netlib core (com.github.fommil.netlib:core:1.1.2 - https://github.com/fommil/netlib-java/core)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.util.Properties;
|
|||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.apache.zeppelin.annotation.ZeppelinApi;
|
||||
import org.apache.zeppelin.annotation.Experimental;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
import org.apache.zeppelin.scheduler.Scheduler;
|
||||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
|
|
@ -203,6 +204,71 @@ public abstract class Interpreter {
|
|||
this.classloaderUrls = classloaderUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* General function to register hook event
|
||||
* @param noteId - Note to bind hook to
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param cmd The code to be executed by the interpreter on given event
|
||||
*/
|
||||
@Experimental
|
||||
public void registerHook(String noteId, String event, String cmd) {
|
||||
InterpreterHookRegistry hooks = interpreterGroup.getInterpreterHookRegistry();
|
||||
String className = getClassName();
|
||||
hooks.register(noteId, className, event, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* registerHook() wrapper for global scope
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
* @param cmd The code to be executed by the interpreter on given event
|
||||
*/
|
||||
@Experimental
|
||||
public void registerHook(String event, String cmd) {
|
||||
registerHook(null, event, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hook code
|
||||
* @param noteId - Note to bind hook to
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public String getHook(String noteId, String event) {
|
||||
InterpreterHookRegistry hooks = interpreterGroup.getInterpreterHookRegistry();
|
||||
String className = getClassName();
|
||||
return hooks.get(noteId, className, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* getHook() wrapper for global scope
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public String getHook(String event) {
|
||||
return getHook(null, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind code from given hook event
|
||||
* @param noteId - Note to bind hook to
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public void unregisterHook(String noteId, String event) {
|
||||
InterpreterHookRegistry hooks = interpreterGroup.getInterpreterHookRegistry();
|
||||
String className = getClassName();
|
||||
hooks.unregister(noteId, className, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* unregisterHook() wrapper for global scope
|
||||
* @param event The type of event to hook to (pre_exec, post_exec)
|
||||
*/
|
||||
@Experimental
|
||||
public void unregisterHook(String event) {
|
||||
unregisterHook(null, event);
|
||||
}
|
||||
|
||||
@ZeppelinApi
|
||||
public Interpreter getInterpreterInTheSameSessionByClassName(String className) {
|
||||
synchronized (interpreterGroup) {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public class InterpreterContext {
|
|||
private AngularObjectRegistry angularObjectRegistry;
|
||||
private ResourcePool resourcePool;
|
||||
private List<InterpreterContextRunner> runners;
|
||||
private String className;
|
||||
|
||||
public InterpreterContext(String noteId,
|
||||
String paragraphId,
|
||||
|
|
@ -131,4 +132,11 @@ public class InterpreterContext {
|
|||
return runners;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import org.apache.zeppelin.scheduler.SchedulerFactory;
|
|||
* and InterpreterGroup will have reference to these all interpreters.
|
||||
*
|
||||
* Remember, list of interpreters are dedicated to a note.
|
||||
* (when InterpreterOption.perNoteSession==true)
|
||||
* (when InterpreterOption.session==true)
|
||||
* So InterpreterGroup internally manages map of [noteId, list of interpreters]
|
||||
*
|
||||
* A InterpreterGroup runs on interpreter process.
|
||||
|
|
@ -45,6 +45,7 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
|
|||
Logger LOGGER = Logger.getLogger(InterpreterGroup.class);
|
||||
|
||||
AngularObjectRegistry angularObjectRegistry;
|
||||
InterpreterHookRegistry hookRegistry;
|
||||
RemoteInterpreterProcess remoteInterpreterProcess; // attached remote interpreter process
|
||||
ResourcePool resourcePool;
|
||||
boolean angularRegistryPushed = false;
|
||||
|
|
@ -118,10 +119,18 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
|
|||
public AngularObjectRegistry getAngularObjectRegistry() {
|
||||
return angularObjectRegistry;
|
||||
}
|
||||
|
||||
|
||||
public void setAngularObjectRegistry(AngularObjectRegistry angularObjectRegistry) {
|
||||
this.angularObjectRegistry = angularObjectRegistry;
|
||||
}
|
||||
|
||||
public InterpreterHookRegistry getInterpreterHookRegistry() {
|
||||
return hookRegistry;
|
||||
}
|
||||
|
||||
public void setInterpreterHookRegistry(InterpreterHookRegistry hookRegistry) {
|
||||
this.hookRegistry = hookRegistry;
|
||||
}
|
||||
|
||||
public RemoteInterpreterProcess getRemoteInterpreterProcess() {
|
||||
return remoteInterpreterProcess;
|
||||
|
|
@ -194,6 +203,14 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
|
|||
LOGGER.info("Destroy interpreter group " + getId() + " for note " + noteId);
|
||||
List<Interpreter> intpForNote = this.get(noteId);
|
||||
destroy(intpForNote);
|
||||
|
||||
if (remoteInterpreterProcess != null) {
|
||||
remoteInterpreterProcess.dereference();
|
||||
if (remoteInterpreterProcess.referenceCount() <= 0) {
|
||||
remoteInterpreterProcess = null;
|
||||
allInterpreterGroups.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -213,6 +230,7 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
|
|||
while (remoteInterpreterProcess.referenceCount() > 0) {
|
||||
remoteInterpreterProcess.dereference();
|
||||
}
|
||||
remoteInterpreterProcess = null;
|
||||
}
|
||||
|
||||
allInterpreterGroups.remove(id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
package org.apache.zeppelin.interpreter;
|
||||
|
||||
/**
|
||||
* An interface for processing custom callback code into the interpreter.
|
||||
*/
|
||||
public interface InterpreterHookListener {
|
||||
/**
|
||||
* Prepends pre-execute hook code to the script that will be interpreted
|
||||
*/
|
||||
public void onPreExecute(String script);
|
||||
|
||||
/**
|
||||
* Appends post-execute hook code to the script that will be interpreted
|
||||
*/
|
||||
public void onPostExecute(String script);
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
package org.apache.zeppelin.interpreter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The InterpreterinterpreterHookRegistry specifies code to be conditionally executed by an
|
||||
* interpreter. The constants defined in this class denote currently
|
||||
* supported events. Each instance is bound to a single InterpreterGroup.
|
||||
* Scope is determined on a per-note basis (except when null for global scope).
|
||||
*/
|
||||
public class InterpreterHookRegistry {
|
||||
public static final String GLOBAL_KEY = "_GLOBAL_";
|
||||
private String interpreterId;
|
||||
private Map<String, Map<String, Map<String, String>>> registry =
|
||||
new HashMap<String, Map<String, Map<String, String>>>();
|
||||
|
||||
/**
|
||||
* hookRegistry constructor.
|
||||
*
|
||||
* @param interpreterId The Id of the InterpreterGroup instance to bind to
|
||||
*/
|
||||
public InterpreterHookRegistry(final String interpreterId) {
|
||||
this.interpreterId = interpreterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interpreterGroup id this instance is bound to
|
||||
*/
|
||||
public String getInterpreterId() {
|
||||
return interpreterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a note to the registry
|
||||
*
|
||||
* @param noteId The Id of the Note instance to add
|
||||
*/
|
||||
public void addNote(String noteId) {
|
||||
synchronized (registry) {
|
||||
if (registry.get(noteId) == null) {
|
||||
registry.put(noteId, new HashMap<String, Map<String, String>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a className to the registry
|
||||
*
|
||||
* @param noteId The note id
|
||||
* @param className The name of the interpreter repl to map the hooks to
|
||||
*/
|
||||
public void addRepl(String noteId, String className) {
|
||||
synchronized (registry) {
|
||||
addNote(noteId);
|
||||
if (registry.get(noteId).get(className) == null) {
|
||||
registry.get(noteId).put(className, new HashMap<String, String>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a hook for a specific event.
|
||||
*
|
||||
* @param noteId Denotes the note this instance belongs to
|
||||
* @param className The name of the interpreter repl to map the hooks to
|
||||
* @param event hook event (see constants defined in this class)
|
||||
* @param cmd Code to be executed by the interpreter
|
||||
*/
|
||||
public void register(String noteId, String className,
|
||||
String event, String cmd) throws IllegalArgumentException {
|
||||
synchronized (registry) {
|
||||
if (noteId == null) {
|
||||
noteId = GLOBAL_KEY;
|
||||
}
|
||||
addRepl(noteId, className);
|
||||
if (!event.equals(HookType.POST_EXEC) && !event.equals(HookType.PRE_EXEC) &&
|
||||
!event.equals(HookType.POST_EXEC_DEV) && !event.equals(HookType.PRE_EXEC_DEV)) {
|
||||
throw new IllegalArgumentException("Must be " + HookType.POST_EXEC + ", " +
|
||||
HookType.POST_EXEC_DEV + ", " +
|
||||
HookType.PRE_EXEC + " or " +
|
||||
HookType.PRE_EXEC_DEV);
|
||||
}
|
||||
registry.get(noteId).get(className).put(event, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a hook for a specific event.
|
||||
*
|
||||
* @param noteId Denotes the note this instance belongs to
|
||||
* @param className The name of the interpreter repl to map the hooks to
|
||||
* @param event hook event (see constants defined in this class)
|
||||
*/
|
||||
public void unregister(String noteId, String className, String event) {
|
||||
synchronized (registry) {
|
||||
if (noteId == null) {
|
||||
noteId = GLOBAL_KEY;
|
||||
}
|
||||
addRepl(noteId, className);
|
||||
registry.get(noteId).get(className).remove(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hook for a specific event.
|
||||
*
|
||||
* @param noteId Denotes the note this instance belongs to
|
||||
* @param className The name of the interpreter repl to map the hooks to
|
||||
* @param event hook event (see constants defined in this class)
|
||||
*/
|
||||
public String get(String noteId, String className, String event) {
|
||||
synchronized (registry) {
|
||||
if (noteId == null) {
|
||||
noteId = GLOBAL_KEY;
|
||||
}
|
||||
addRepl(noteId, className);
|
||||
return registry.get(noteId).get(className).get(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Container for hook event type constants
|
||||
*/
|
||||
public static final class HookType {
|
||||
// Execute the hook code PRIOR to main paragraph code execution
|
||||
public static final String PRE_EXEC = "pre_exec";
|
||||
|
||||
// Execute the hook code AFTER main paragraph code execution
|
||||
public static final String POST_EXEC = "post_exec";
|
||||
|
||||
// Same as above but reserved for interpreter developers, in order to allow
|
||||
// notebook users to use the above without overwriting registry settings
|
||||
// that are initialized directly in subclasses of Interpreter.
|
||||
public static final String PRE_EXEC_DEV = "pre_exec_dev";
|
||||
public static final String POST_EXEC_DEV = "post_exec_dev";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -147,4 +147,34 @@ public class LazyOpenInterpreter
|
|||
public void setClassloaderUrls(URL [] urls) {
|
||||
intp.setClassloaderUrls(urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHook(String noteId, String event, String cmd) {
|
||||
intp.registerHook(noteId, event, cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHook(String event, String cmd) {
|
||||
intp.registerHook(event, cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHook(String noteId, String event) {
|
||||
return intp.getHook(noteId, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHook(String event) {
|
||||
return intp.getHook(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterHook(String noteId, String event) {
|
||||
intp.unregisterHook(noteId, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterHook(String event) {
|
||||
intp.unregisterHook(event);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ public class RemoteInterpreter extends Interpreter {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("st:\n{}", st);
|
||||
}
|
||||
|
||||
FormType form = getFormType();
|
||||
RemoteInterpreterProcess interpreterProcess = getInterpreterProcess();
|
||||
Client client = null;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import org.apache.zeppelin.dep.DependencyResolver;
|
|||
import org.apache.zeppelin.display.*;
|
||||
import org.apache.zeppelin.helium.*;
|
||||
import org.apache.zeppelin.interpreter.*;
|
||||
import org.apache.zeppelin.interpreter.InterpreterHookRegistry.HookType;
|
||||
import org.apache.zeppelin.interpreter.InterpreterHookListener;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.apache.zeppelin.interpreter.dev.ZeppelinDevServer;
|
||||
import org.apache.zeppelin.interpreter.thrift.*;
|
||||
|
|
@ -60,6 +62,7 @@ public class RemoteInterpreterServer
|
|||
|
||||
InterpreterGroup interpreterGroup;
|
||||
AngularObjectRegistry angularObjectRegistry;
|
||||
InterpreterHookRegistry hookRegistry;
|
||||
DistributedResourcePool resourcePool;
|
||||
private ApplicationLoader appLoader;
|
||||
|
||||
|
|
@ -152,7 +155,9 @@ public class RemoteInterpreterServer
|
|||
if (interpreterGroup == null) {
|
||||
interpreterGroup = new InterpreterGroup(interpreterGroupId);
|
||||
angularObjectRegistry = new AngularObjectRegistry(interpreterGroup.getId(), this);
|
||||
hookRegistry = new InterpreterHookRegistry(interpreterGroup.getId());
|
||||
resourcePool = new DistributedResourcePool(interpreterGroup.getId(), eventClient);
|
||||
interpreterGroup.setInterpreterHookRegistry(hookRegistry);
|
||||
interpreterGroup.setAngularObjectRegistry(angularObjectRegistry);
|
||||
interpreterGroup.setResourcePool(resourcePool);
|
||||
|
||||
|
|
@ -290,6 +295,7 @@ public class RemoteInterpreterServer
|
|||
}
|
||||
Interpreter intp = getInterpreter(noteId, className);
|
||||
InterpreterContext context = convert(interpreterContext);
|
||||
context.setClassName(intp.getClassName());
|
||||
|
||||
Scheduler scheduler = intp.getScheduler();
|
||||
InterpretJobListener jobListener = new InterpretJobListener();
|
||||
|
|
@ -383,10 +389,61 @@ public class RemoteInterpreterServer
|
|||
return infos;
|
||||
}
|
||||
|
||||
private void processInterpreterHooks(final String noteId) {
|
||||
InterpreterHookListener hookListener = new InterpreterHookListener() {
|
||||
@Override
|
||||
public void onPreExecute(String script) {
|
||||
String cmdDev = interpreter.getHook(noteId, HookType.PRE_EXEC_DEV);
|
||||
String cmdUser = interpreter.getHook(noteId, HookType.PRE_EXEC);
|
||||
|
||||
// User defined hook should be executed before dev hook
|
||||
List<String> cmds = Arrays.asList(cmdDev, cmdUser);
|
||||
for (String cmd : cmds) {
|
||||
if (cmd != null) {
|
||||
script = cmd + '\n' + script;
|
||||
}
|
||||
}
|
||||
|
||||
InterpretJob.this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute(String script) {
|
||||
String cmdDev = interpreter.getHook(noteId, HookType.POST_EXEC_DEV);
|
||||
String cmdUser = interpreter.getHook(noteId, HookType.POST_EXEC);
|
||||
|
||||
// User defined hook should be executed after dev hook
|
||||
List<String> cmds = Arrays.asList(cmdUser, cmdDev);
|
||||
for (String cmd : cmds) {
|
||||
if (cmd != null) {
|
||||
script += '\n' + cmd;
|
||||
}
|
||||
}
|
||||
|
||||
InterpretJob.this.script = script;
|
||||
}
|
||||
};
|
||||
hookListener.onPreExecute(script);
|
||||
hookListener.onPostExecute(script);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object jobRun() throws Throwable {
|
||||
try {
|
||||
InterpreterContext.set(context);
|
||||
|
||||
// Open the interpreter instance prior to calling interpret().
|
||||
// This is necessary because the earliest we can register a hook
|
||||
// is from within the open() method.
|
||||
LazyOpenInterpreter lazy = (LazyOpenInterpreter) interpreter;
|
||||
if (!lazy.isOpen()) {
|
||||
lazy.open();
|
||||
}
|
||||
|
||||
// Add hooks to script from registry.
|
||||
// Global scope first, followed by notebook scope
|
||||
processInterpreterHooks(null);
|
||||
processInterpreterHooks(context.getNoteId());
|
||||
InterpreterResult result = interpreter.interpret(script, context);
|
||||
|
||||
// data from context.out is prepended to InterpreterResult if both defined
|
||||
|
|
|
|||
|
|
@ -18,13 +18,20 @@
|
|||
|
||||
package org.apache.zeppelin.user;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/***
|
||||
*
|
||||
*/
|
||||
public class AuthenticationInfo {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationInfo.class);
|
||||
String user;
|
||||
String ticket;
|
||||
UserCredentials userCredentials;
|
||||
public static final AuthenticationInfo ANONYMOUS = new AuthenticationInfo("anonymous",
|
||||
"anonymous");
|
||||
|
||||
public AuthenticationInfo() {}
|
||||
|
||||
|
|
@ -66,4 +73,17 @@ public class AuthenticationInfo {
|
|||
this.userCredentials = userCredentials;
|
||||
}
|
||||
|
||||
public static boolean isAnonymous(AuthenticationInfo subject) {
|
||||
if (subject == null) {
|
||||
LOG.warn("Subject is null, assuming anonymous. "
|
||||
+ "Not recommended to use subject as null except in tests");
|
||||
return true;
|
||||
}
|
||||
return subject.isAnonymous();
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
return ANONYMOUS.equals(this) || "anonymous".equalsIgnoreCase(this.getUser())
|
||||
|| StringUtils.isEmpty(this.getUser());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
package org.apache.zeppelin.interpreter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class InterpreterHookRegistryTest {
|
||||
|
||||
@Test
|
||||
public void testBasic() {
|
||||
final String PRE_EXEC = InterpreterHookRegistry.HookType.PRE_EXEC;
|
||||
final String POST_EXEC = InterpreterHookRegistry.HookType.POST_EXEC;
|
||||
final String PRE_EXEC_DEV = InterpreterHookRegistry.HookType.PRE_EXEC_DEV;
|
||||
final String POST_EXEC_DEV = InterpreterHookRegistry.HookType.POST_EXEC_DEV;
|
||||
final String GLOBAL_KEY = InterpreterHookRegistry.GLOBAL_KEY;
|
||||
final String noteId = "note";
|
||||
final String className = "class";
|
||||
final String preExecHook = "pre";
|
||||
final String postExecHook = "post";
|
||||
InterpreterHookRegistry registry = new InterpreterHookRegistry("intpId");
|
||||
|
||||
// Test register()
|
||||
registry.register(noteId, className, PRE_EXEC, preExecHook);
|
||||
registry.register(noteId, className, POST_EXEC, postExecHook);
|
||||
registry.register(noteId, className, PRE_EXEC_DEV, preExecHook);
|
||||
registry.register(noteId, className, POST_EXEC_DEV, postExecHook);
|
||||
|
||||
// Test get()
|
||||
assertEquals(registry.get(noteId, className, PRE_EXEC), preExecHook);
|
||||
assertEquals(registry.get(noteId, className, POST_EXEC), postExecHook);
|
||||
assertEquals(registry.get(noteId, className, PRE_EXEC_DEV), preExecHook);
|
||||
assertEquals(registry.get(noteId, className, POST_EXEC_DEV), postExecHook);
|
||||
|
||||
// Test Unregister
|
||||
registry.unregister(noteId, className, PRE_EXEC);
|
||||
registry.unregister(noteId, className, POST_EXEC);
|
||||
registry.unregister(noteId, className, PRE_EXEC_DEV);
|
||||
registry.unregister(noteId, className, POST_EXEC_DEV);
|
||||
assertNull(registry.get(noteId, className, PRE_EXEC));
|
||||
assertNull(registry.get(noteId, className, POST_EXEC));
|
||||
assertNull(registry.get(noteId, className, PRE_EXEC_DEV));
|
||||
assertNull(registry.get(noteId, className, POST_EXEC_DEV));
|
||||
|
||||
// Test Global Scope
|
||||
registry.register(null, className, PRE_EXEC, preExecHook);
|
||||
assertEquals(registry.get(GLOBAL_KEY, className, PRE_EXEC), preExecHook);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testValidEventCode() {
|
||||
InterpreterHookRegistry registry = new InterpreterHookRegistry("intpId");
|
||||
|
||||
// Test that only valid event codes ("pre_exec", "post_exec") are accepted
|
||||
registry.register("foo", "bar", "baz", "whatever");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
|
|
@ -36,6 +37,7 @@ import org.apache.shiro.authc.UsernamePasswordToken;
|
|||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -135,6 +137,7 @@ public class ZeppelinHubRealm extends AuthorizingRealm {
|
|||
}
|
||||
responseBody = put.getResponseBodyAsString();
|
||||
put.releaseConnection();
|
||||
|
||||
} catch (IOException e) {
|
||||
LOG.error("Cannot login user", e);
|
||||
throw new AuthenticationException(e.getMessage());
|
||||
|
|
@ -147,6 +150,13 @@ public class ZeppelinHubRealm extends AuthorizingRealm {
|
|||
LOG.error("Cannot deserialize ZeppelinHub response to User instance", e);
|
||||
throw new AuthenticationException("Cannot login to ZeppelinHub");
|
||||
}
|
||||
|
||||
/* TODO(khalid): add proper roles and add listener */
|
||||
HashSet<String> userAndRoles = new HashSet<String>();
|
||||
userAndRoles.add(account.login);
|
||||
ZeppelinServer.notebookWsServer.broadcastReloadedNoteList(
|
||||
new org.apache.zeppelin.user.AuthenticationInfo(account.login), userAndRoles);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import com.google.common.reflect.TypeToken;
|
|||
import com.google.gson.Gson;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.scheduler.Job;
|
||||
import org.apache.zeppelin.utils.InterpreterBindingUtils;
|
||||
import org.quartz.CronExpression;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -52,7 +51,7 @@ import org.apache.zeppelin.notebook.NotebookAuthorization;
|
|||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
import org.apache.zeppelin.rest.message.CronRequest;
|
||||
import org.apache.zeppelin.types.InterpreterSettingsList;
|
||||
import org.apache.zeppelin.rest.message.NewNotebookRequest;
|
||||
import org.apache.zeppelin.rest.message.NewNoteRequest;
|
||||
import org.apache.zeppelin.rest.message.NewParagraphRequest;
|
||||
import org.apache.zeppelin.rest.message.RunParagraphWithParametersRequest;
|
||||
import org.apache.zeppelin.search.SearchService;
|
||||
|
|
@ -62,7 +61,7 @@ import org.apache.zeppelin.user.AuthenticationInfo;
|
|||
import org.apache.zeppelin.utils.SecurityUtils;
|
||||
|
||||
/**
|
||||
* Rest api endpoint for the noteBook.
|
||||
* Rest api endpoint for the notebook.
|
||||
*/
|
||||
@Path("/notebook")
|
||||
@Produces("application/json")
|
||||
|
|
@ -71,7 +70,7 @@ public class NotebookRestApi {
|
|||
Gson gson = new Gson();
|
||||
private Notebook notebook;
|
||||
private NotebookServer notebookServer;
|
||||
private SearchService notebookIndex;
|
||||
private SearchService noteSearchService;
|
||||
private NotebookAuthorization notebookAuthorization;
|
||||
|
||||
public NotebookRestApi() {
|
||||
|
|
@ -80,7 +79,7 @@ public class NotebookRestApi {
|
|||
public NotebookRestApi(Notebook notebook, NotebookServer notebookServer, SearchService search) {
|
||||
this.notebook = notebook;
|
||||
this.notebookServer = notebookServer;
|
||||
this.notebookIndex = search;
|
||||
this.noteSearchService = search;
|
||||
this.notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
}
|
||||
|
||||
|
|
@ -114,10 +113,6 @@ public class NotebookRestApi {
|
|||
@ZeppelinApi
|
||||
public Response putNotePermissions(@PathParam("noteId") String noteId, String req)
|
||||
throws IOException {
|
||||
/**
|
||||
* TODO(jl): Fixed the type of HashSet
|
||||
* https://issues.apache.org/jira/browse/ZEPPELIN-1162
|
||||
*/
|
||||
HashMap<String, HashSet<String>> permMap =
|
||||
gson.fromJson(req, new TypeToken<HashMap<String, HashSet<String>>>() {
|
||||
}.getType());
|
||||
|
|
@ -162,6 +157,7 @@ public class NotebookRestApi {
|
|||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
note.persist(subject);
|
||||
notebookServer.broadcastNote(note);
|
||||
notebookServer.broadcastNoteList(subject, userAndRoles);
|
||||
return new JsonResponse<>(Status.OK).build();
|
||||
}
|
||||
|
||||
|
|
@ -176,12 +172,12 @@ public class NotebookRestApi {
|
|||
public Response bind(@PathParam("noteId") String noteId, String req) throws IOException {
|
||||
List<String> settingIdList = gson.fromJson(req, new TypeToken<List<String>>() {
|
||||
}.getType());
|
||||
notebook.bindInterpretersToNote(noteId, settingIdList);
|
||||
notebook.bindInterpretersToNote(SecurityUtils.getPrincipal(), noteId, settingIdList);
|
||||
return new JsonResponse<>(Status.OK).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* list binded setting
|
||||
* list bound setting
|
||||
*/
|
||||
@GET
|
||||
@Path("interpreter/bind/{noteId}")
|
||||
|
|
@ -196,17 +192,18 @@ public class NotebookRestApi {
|
|||
@GET
|
||||
@Path("/")
|
||||
@ZeppelinApi
|
||||
public Response getNotebookList() throws IOException {
|
||||
public Response getNoteList() throws IOException {
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
List<Map<String, String>> notesInfo = notebookServer.generateNotebooksInfo(false, subject);
|
||||
List<Map<String, String>> notesInfo = notebookServer.generateNotesInfo(false, subject,
|
||||
SecurityUtils.getRoles());
|
||||
return new JsonResponse<>(Status.OK, "", notesInfo).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{notebookId}")
|
||||
@Path("{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response getNotebook(@PathParam("notebookId") String notebookId) throws IOException {
|
||||
Note note = notebook.getNote(notebookId);
|
||||
public Response getNote(@PathParam("noteId") String noteId) throws IOException {
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -222,9 +219,9 @@ public class NotebookRestApi {
|
|||
* @throws IOException
|
||||
*/
|
||||
@GET
|
||||
@Path("export/{id}")
|
||||
@Path("export/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response exportNoteBook(@PathParam("id") String noteId) throws IOException {
|
||||
public Response exportNote(@PathParam("noteId") String noteId) throws IOException {
|
||||
String exportJson = notebook.exportNote(noteId);
|
||||
return new JsonResponse<>(Status.OK, "", exportJson).build();
|
||||
}
|
||||
|
|
@ -232,14 +229,14 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* import new note REST API
|
||||
*
|
||||
* @param req - notebook Json
|
||||
* @param req - note Json
|
||||
* @return JSON with new note ID
|
||||
* @throws IOException
|
||||
*/
|
||||
@POST
|
||||
@Path("import")
|
||||
@ZeppelinApi
|
||||
public Response importNotebook(String req) throws IOException {
|
||||
public Response importNote(String req) throws IOException {
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
Note newNote = notebook.importNote(req, null, subject);
|
||||
return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build();
|
||||
|
|
@ -256,8 +253,8 @@ public class NotebookRestApi {
|
|||
@Path("/")
|
||||
@ZeppelinApi
|
||||
public Response createNote(String message) throws IOException {
|
||||
LOG.info("Create new notebook by JSON {}", message);
|
||||
NewNotebookRequest request = gson.fromJson(message, NewNotebookRequest.class);
|
||||
LOG.info("Create new note by JSON {}", message);
|
||||
NewNoteRequest request = gson.fromJson(message, NewNoteRequest.class);
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
Note note = notebook.createNote(subject);
|
||||
List<NewParagraphRequest> initialParagraphs = request.getParagraphs();
|
||||
|
|
@ -277,56 +274,56 @@ public class NotebookRestApi {
|
|||
note.setName(noteName);
|
||||
note.persist(subject);
|
||||
notebookServer.broadcastNote(note);
|
||||
notebookServer.broadcastNoteList(subject);
|
||||
notebookServer.broadcastNoteList(subject, SecurityUtils.getRoles());
|
||||
return new JsonResponse<>(Status.CREATED, "", note.getId()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete note REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{notebookId}")
|
||||
@Path("{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response deleteNote(@PathParam("notebookId") String notebookId) throws IOException {
|
||||
LOG.info("Delete notebook {} ", notebookId);
|
||||
public Response deleteNote(@PathParam("noteId") String noteId) throws IOException {
|
||||
LOG.info("Delete note {} ", noteId);
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
if (!(notebookId.isEmpty())) {
|
||||
Note note = notebook.getNote(notebookId);
|
||||
if (!(noteId.isEmpty())) {
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note != null) {
|
||||
notebook.removeNote(notebookId, subject);
|
||||
notebook.removeNote(noteId, subject);
|
||||
}
|
||||
}
|
||||
|
||||
notebookServer.broadcastNoteList(subject);
|
||||
notebookServer.broadcastNoteList(subject, SecurityUtils.getRoles());
|
||||
return new JsonResponse<>(Status.OK, "").build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone note REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.CREATED
|
||||
* @throws IOException, CloneNotSupportedException, IllegalArgumentException
|
||||
*/
|
||||
@POST
|
||||
@Path("{notebookId}")
|
||||
@Path("{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response cloneNote(@PathParam("notebookId") String notebookId, String message)
|
||||
public Response cloneNote(@PathParam("noteId") String noteId, String message)
|
||||
throws IOException, CloneNotSupportedException, IllegalArgumentException {
|
||||
LOG.info("clone notebook by JSON {}", message);
|
||||
NewNotebookRequest request = gson.fromJson(message, NewNotebookRequest.class);
|
||||
LOG.info("clone note by JSON {}", message);
|
||||
NewNoteRequest request = gson.fromJson(message, NewNoteRequest.class);
|
||||
String newNoteName = null;
|
||||
if (request != null) {
|
||||
newNoteName = request.getName();
|
||||
}
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
Note newNote = notebook.cloneNote(notebookId, newNoteName, subject);
|
||||
Note newNote = notebook.cloneNote(noteId, newNoteName, subject);
|
||||
notebookServer.broadcastNote(newNote);
|
||||
notebookServer.broadcastNoteList(subject);
|
||||
notebookServer.broadcastNoteList(subject, SecurityUtils.getRoles());
|
||||
return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build();
|
||||
}
|
||||
|
||||
|
|
@ -338,13 +335,13 @@ public class NotebookRestApi {
|
|||
* @throws IOException
|
||||
*/
|
||||
@POST
|
||||
@Path("{notebookId}/paragraph")
|
||||
@Path("{noteId}/paragraph")
|
||||
@ZeppelinApi
|
||||
public Response insertParagraph(@PathParam("notebookId") String notebookId, String message)
|
||||
public Response insertParagraph(@PathParam("noteId") String noteId, String message)
|
||||
throws IOException {
|
||||
LOG.info("insert paragraph {} {}", notebookId, message);
|
||||
LOG.info("insert paragraph {} {}", noteId, message);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -370,18 +367,18 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* Get paragraph REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with information of the paragraph
|
||||
* @throws IOException
|
||||
*/
|
||||
@GET
|
||||
@Path("{notebookId}/paragraph/{paragraphId}")
|
||||
@Path("{noteId}/paragraph/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response getParagraph(@PathParam("notebookId") String notebookId,
|
||||
public Response getParagraph(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId) throws IOException {
|
||||
LOG.info("get paragraph {} {}", notebookId, paragraphId);
|
||||
LOG.info("get paragraph {} {}", noteId, paragraphId);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -402,14 +399,14 @@ public class NotebookRestApi {
|
|||
* @throws IOException
|
||||
*/
|
||||
@POST
|
||||
@Path("{notebookId}/paragraph/{paragraphId}/move/{newIndex}")
|
||||
@Path("{noteId}/paragraph/{paragraphId}/move/{newIndex}")
|
||||
@ZeppelinApi
|
||||
public Response moveParagraph(@PathParam("notebookId") String notebookId,
|
||||
public Response moveParagraph(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId, @PathParam("newIndex") String newIndex)
|
||||
throws IOException {
|
||||
LOG.info("move paragraph {} {} {}", notebookId, paragraphId, newIndex);
|
||||
LOG.info("move paragraph {} {} {}", noteId, paragraphId, newIndex);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -435,18 +432,18 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* Delete paragraph REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{notebookId}/paragraph/{paragraphId}")
|
||||
@Path("{noteId}/paragraph/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response deleteParagraph(@PathParam("notebookId") String notebookId,
|
||||
public Response deleteParagraph(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId) throws IOException {
|
||||
LOG.info("delete paragraph {} {}", notebookId, paragraphId);
|
||||
LOG.info("delete paragraph {} {}", noteId, paragraphId);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -457,7 +454,7 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
note.removeParagraph(paragraphId);
|
||||
note.removeParagraph(SecurityUtils.getPrincipal(), paragraphId);
|
||||
note.persist(subject);
|
||||
notebookServer.broadcastNote(note);
|
||||
|
||||
|
|
@ -465,19 +462,19 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Run notebook jobs REST API
|
||||
* Run note jobs REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@POST
|
||||
@Path("job/{notebookId}")
|
||||
@Path("job/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response runNoteJobs(@PathParam("notebookId") String notebookId)
|
||||
public Response runNoteJobs(@PathParam("noteId") String noteId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("run notebook jobs {} ", notebookId);
|
||||
Note note = notebook.getNote(notebookId);
|
||||
LOG.info("run note jobs {} ", noteId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -494,19 +491,19 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stop(delete) notebook jobs REST API
|
||||
* Stop(delete) note jobs REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("job/{notebookId}")
|
||||
@Path("job/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response stopNoteJobs(@PathParam("notebookId") String notebookId)
|
||||
public Response stopNoteJobs(@PathParam("noteId") String noteId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("stop notebook jobs {} ", notebookId);
|
||||
Note note = notebook.getNote(notebookId);
|
||||
LOG.info("stop note jobs {} ", noteId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -520,19 +517,19 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get notebook job status REST API
|
||||
* Get note job status REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@GET
|
||||
@Path("job/{notebookId}")
|
||||
@Path("job/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response getNoteJobStatus(@PathParam("notebookId") String notebookId)
|
||||
public Response getNoteJobStatus(@PathParam("noteId") String noteId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("get notebook job status.");
|
||||
Note note = notebook.getNote(notebookId);
|
||||
LOG.info("get note job status.");
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -541,21 +538,21 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get notebook paragraph job status REST API
|
||||
* Get note paragraph job status REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @param paragraphId ID of Paragraph
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@GET
|
||||
@Path("job/{notebookId}/{paragraphId}")
|
||||
@Path("job/{noteId}/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response getNoteParagraphJobStatus(@PathParam("notebookId") String notebookId,
|
||||
public Response getNoteParagraphJobStatus(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("get notebook paragraph job status.");
|
||||
Note note = notebook.getNote(notebookId);
|
||||
LOG.info("get note paragraph job status.");
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -578,14 +575,14 @@ public class NotebookRestApi {
|
|||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@POST
|
||||
@Path("job/{notebookId}/{paragraphId}")
|
||||
@Path("job/{noteId}/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response runParagraph(@PathParam("notebookId") String notebookId,
|
||||
public Response runParagraph(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId, String message)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("run paragraph job asynchronously {} {} {}", notebookId, paragraphId, message);
|
||||
LOG.info("run paragraph job asynchronously {} {} {}", noteId, paragraphId, message);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -598,6 +595,11 @@ public class NotebookRestApi {
|
|||
// handle params if presented
|
||||
handleParagraphParams(message, note, paragraph);
|
||||
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
|
||||
paragraph.setAuthenticationInfo(subject);
|
||||
note.persist(subject);
|
||||
|
||||
note.run(paragraph.getId());
|
||||
return new JsonResponse<>(Status.OK).build();
|
||||
}
|
||||
|
|
@ -614,9 +616,9 @@ public class NotebookRestApi {
|
|||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@POST
|
||||
@Path("run/{notebookId}/{paragraphId}")
|
||||
@Path("run/{noteId}/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response runParagraphSynchronously(@PathParam("notebookId") String noteId,
|
||||
public Response runParagraphSynchronously(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId,
|
||||
String message) throws
|
||||
IOException, IllegalArgumentException {
|
||||
|
|
@ -653,22 +655,18 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* Stop(delete) paragraph job REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @param paragraphId ID of Paragraph
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("job/{notebookId}/{paragraphId}")
|
||||
@Path("job/{noteId}/{paragraphId}")
|
||||
@ZeppelinApi
|
||||
public Response stopParagraph(@PathParam("notebookId") String notebookId,
|
||||
public Response stopParagraph(@PathParam("noteId") String noteId,
|
||||
@PathParam("paragraphId") String paragraphId) throws IOException, IllegalArgumentException {
|
||||
/**
|
||||
* TODO(jl): Fixed notebookId to noteId
|
||||
* https://issues.apache.org/jira/browse/ZEPPELIN-1163
|
||||
*/
|
||||
LOG.info("stop paragraph job {} ", notebookId);
|
||||
Note note = notebook.getNote(notebookId);
|
||||
LOG.info("stop paragraph job {} ", noteId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -689,16 +687,15 @@ public class NotebookRestApi {
|
|||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@POST
|
||||
@Path("cron/{notebookId}")
|
||||
@Path("cron/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response registerCronJob(@PathParam("notebookId") String notebookId, String message)
|
||||
public Response registerCronJob(@PathParam("noteId") String noteId, String message)
|
||||
throws IOException, IllegalArgumentException {
|
||||
// TODO(jl): Fixed notebookId to noteId
|
||||
LOG.info("Register cron job note={} request cron msg={}", notebookId, message);
|
||||
LOG.info("Register cron job note={} request cron msg={}", noteId, message);
|
||||
|
||||
CronRequest request = gson.fromJson(message, CronRequest.class);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -718,19 +715,18 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* Remove cron job REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@DELETE
|
||||
@Path("cron/{notebookId}")
|
||||
@Path("cron/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response removeCronJob(@PathParam("notebookId") String notebookId)
|
||||
public Response removeCronJob(@PathParam("noteId") String noteId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
// TODO(jl): Fixed notebookId to noteId
|
||||
LOG.info("Remove cron job note {}", notebookId);
|
||||
LOG.info("Remove cron job note {}", noteId);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -746,19 +742,18 @@ public class NotebookRestApi {
|
|||
/**
|
||||
* Get cron job REST API
|
||||
*
|
||||
* @param notebookId ID of Notebook
|
||||
* @param noteId ID of Note
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
*/
|
||||
@GET
|
||||
@Path("cron/{notebookId}")
|
||||
@Path("cron/{noteId}")
|
||||
@ZeppelinApi
|
||||
public Response getCronJob(@PathParam("notebookId") String notebookId)
|
||||
public Response getCronJob(@PathParam("noteId") String noteId)
|
||||
throws IOException, IllegalArgumentException {
|
||||
// TODO(jl): Fixed notebookId to noteId
|
||||
LOG.info("Get cron job note {}", notebookId);
|
||||
LOG.info("Get cron job note {}", noteId);
|
||||
|
||||
Note note = notebook.getNote(notebookId);
|
||||
Note note = notebook.getNote(noteId);
|
||||
if (note == null) {
|
||||
return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
|
||||
}
|
||||
|
|
@ -767,7 +762,7 @@ public class NotebookRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get notebook jobs for job manager
|
||||
* Get note jobs for job manager
|
||||
*
|
||||
* @return JSON with status.OK
|
||||
* @throws IOException, IllegalArgumentException
|
||||
|
|
@ -775,22 +770,22 @@ public class NotebookRestApi {
|
|||
@GET
|
||||
@Path("jobmanager/")
|
||||
@ZeppelinApi
|
||||
public Response getJobListforNotebook() throws IOException, IllegalArgumentException {
|
||||
LOG.info("Get notebook jobs for job manager");
|
||||
public Response getJobListforNote() throws IOException, IllegalArgumentException {
|
||||
LOG.info("Get note jobs for job manager");
|
||||
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
List<Map<String, Object>> notebookJobs = notebook
|
||||
.getJobListByUnixTime(false, 0, subject);
|
||||
List<Map<String, Object>> noteJobs = notebook
|
||||
.getJobListByUnixTime(false, 0, subject);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
response.put("jobs", noteJobs);
|
||||
|
||||
return new JsonResponse<>(Status.OK, response).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updated notebook jobs for job manager
|
||||
* Get updated note jobs for job manager
|
||||
*
|
||||
* Return the `Note` change information within the post unix timestamp.
|
||||
*
|
||||
|
|
@ -800,18 +795,18 @@ public class NotebookRestApi {
|
|||
@GET
|
||||
@Path("jobmanager/{lastUpdateUnixtime}/")
|
||||
@ZeppelinApi
|
||||
public Response getUpdatedJobListforNotebook(
|
||||
public Response getUpdatedJobListforNote(
|
||||
@PathParam("lastUpdateUnixtime") long lastUpdateUnixTime)
|
||||
throws IOException, IllegalArgumentException {
|
||||
LOG.info("Get updated notebook jobs lastUpdateTime {}", lastUpdateUnixTime);
|
||||
LOG.info("Get updated note jobs lastUpdateTime {}", lastUpdateUnixTime);
|
||||
|
||||
List<Map<String, Object>> notebookJobs;
|
||||
List<Map<String, Object>> noteJobs;
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
notebookJobs = notebook.getJobListByUnixTime(false, lastUpdateUnixTime, subject);
|
||||
noteJobs = notebook.getJobListByUnixTime(false, lastUpdateUnixTime, subject);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
response.put("jobs", noteJobs);
|
||||
|
||||
return new JsonResponse<>(Status.OK, response).build();
|
||||
}
|
||||
|
|
@ -823,25 +818,25 @@ public class NotebookRestApi {
|
|||
@Path("search")
|
||||
@ZeppelinApi
|
||||
public Response search(@QueryParam("q") String queryTerm) {
|
||||
LOG.info("Searching notebooks for: {}", queryTerm);
|
||||
LOG.info("Searching notes for: {}", queryTerm);
|
||||
String principal = SecurityUtils.getPrincipal();
|
||||
HashSet<String> roles = SecurityUtils.getRoles();
|
||||
HashSet<String> userAndRoles = new HashSet<>();
|
||||
userAndRoles.add(principal);
|
||||
userAndRoles.addAll(roles);
|
||||
List<Map<String, String>> notebooksFound = notebookIndex.query(queryTerm);
|
||||
for (int i = 0; i < notebooksFound.size(); i++) {
|
||||
String[] Id = notebooksFound.get(i).get("id").split("/", 2);
|
||||
List<Map<String, String>> notesFound = noteSearchService.query(queryTerm);
|
||||
for (int i = 0; i < notesFound.size(); i++) {
|
||||
String[] Id = notesFound.get(i).get("id").split("/", 2);
|
||||
String noteId = Id[0];
|
||||
if (!notebookAuthorization.isOwner(noteId, userAndRoles) &&
|
||||
!notebookAuthorization.isReader(noteId, userAndRoles) &&
|
||||
!notebookAuthorization.isWriter(noteId, userAndRoles)) {
|
||||
notebooksFound.remove(i);
|
||||
notesFound.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
LOG.info("{} notebooks found", notebooksFound.size());
|
||||
return new JsonResponse<>(Status.OK, notebooksFound).build();
|
||||
LOG.info("{} notes found", notesFound.size());
|
||||
return new JsonResponse<>(Status.OK, notesFound).build();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -850,7 +845,7 @@ public class NotebookRestApi {
|
|||
// handle params if presented
|
||||
if (!StringUtils.isEmpty(message)) {
|
||||
RunParagraphWithParametersRequest request =
|
||||
gson.fromJson(message, RunParagraphWithParametersRequest.class);
|
||||
gson.fromJson(message, RunParagraphWithParametersRequest.class);
|
||||
Map<String, Object> paramsForUpdating = request.getParams();
|
||||
if (paramsForUpdating != null) {
|
||||
paragraph.settings.getParams().putAll(paramsForUpdating);
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ import java.util.Map;
|
|||
import org.apache.zeppelin.interpreter.InterpreterOption;
|
||||
|
||||
/**
|
||||
* NewNotebookRequest rest api request message
|
||||
* NewNoteRequest rest api request message
|
||||
*
|
||||
*/
|
||||
public class NewNotebookRequest {
|
||||
public class NewNoteRequest {
|
||||
String name;
|
||||
List<NewParagraphRequest> paragraphs;
|
||||
|
||||
public NewNotebookRequest (){
|
||||
public NewNoteRequest (){
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ public class ZeppelinServer extends Application {
|
|||
private SchedulerFactory schedulerFactory;
|
||||
private InterpreterFactory replFactory;
|
||||
private NotebookRepo notebookRepo;
|
||||
private SearchService notebookIndex;
|
||||
private SearchService noteSearchService;
|
||||
private NotebookAuthorization notebookAuthorization;
|
||||
private Credentials credentials;
|
||||
private DependencyResolver depResolver;
|
||||
|
|
@ -89,14 +89,14 @@ public class ZeppelinServer extends Application {
|
|||
this.heliumApplicationFactory = new HeliumApplicationFactory();
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
this.replFactory = new InterpreterFactory(conf, notebookWsServer,
|
||||
notebookWsServer, heliumApplicationFactory, depResolver);
|
||||
notebookWsServer, heliumApplicationFactory, depResolver, SecurityUtils.isAuthenticated());
|
||||
this.notebookRepo = new NotebookRepoSync(conf);
|
||||
this.notebookIndex = new LuceneSearch();
|
||||
this.notebookAuthorization = new NotebookAuthorization(conf);
|
||||
this.noteSearchService = new LuceneSearch();
|
||||
this.notebookAuthorization = NotebookAuthorization.init(conf);
|
||||
this.credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath());
|
||||
notebook = new Notebook(conf,
|
||||
notebookRepo, schedulerFactory, replFactory, notebookWsServer,
|
||||
notebookIndex, notebookAuthorization, credentials);
|
||||
noteSearchService, notebookAuthorization, credentials);
|
||||
|
||||
// to update notebook from application event from remote process.
|
||||
heliumApplicationFactory.setNotebook(notebook);
|
||||
|
|
@ -304,7 +304,8 @@ public class ZeppelinServer extends Application {
|
|||
ZeppelinRestApi root = new ZeppelinRestApi();
|
||||
singletons.add(root);
|
||||
|
||||
NotebookRestApi notebookApi = new NotebookRestApi(notebook, notebookWsServer, notebookIndex);
|
||||
NotebookRestApi notebookApi
|
||||
= new NotebookRestApi(notebook, notebookWsServer, noteSearchService);
|
||||
singletons.add(notebookApi);
|
||||
|
||||
HeliumRestApi heliumApi = new HeliumRestApi(helium, heliumApplicationFactory, notebook);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import com.google.common.base.Strings;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.vfs2.FileSystemException;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
|
|
@ -60,6 +59,7 @@ import java.io.IOException;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
|
|
@ -86,6 +86,8 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
|
||||
final Map<String, List<NotebookSocket>> noteSocketMap = new HashMap<>();
|
||||
final Queue<NotebookSocket> connectedSockets = new ConcurrentLinkedQueue<>();
|
||||
final Map<String, Queue<NotebookSocket>> userConnectedSockets =
|
||||
new ConcurrentHashMap<String, Queue<NotebookSocket>>();
|
||||
|
||||
private Notebook notebook() {
|
||||
return ZeppelinServer.notebook;
|
||||
|
|
@ -131,7 +133,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("RECEIVE MSG = " + messagereceived);
|
||||
}
|
||||
|
||||
|
||||
String ticket = TicketContainer.instance.getTicket(messagereceived.principal);
|
||||
if (ticket != null && !ticket.equals(messagereceived.ticket)){
|
||||
/* not to pollute logs, log instead of exception */
|
||||
|
|
@ -161,15 +163,18 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
userAndRoles.addAll(roles);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(conn.getUser())) {
|
||||
addUserConnection(messagereceived.principal, conn);
|
||||
}
|
||||
AuthenticationInfo subject = new AuthenticationInfo(messagereceived.principal);
|
||||
|
||||
/** Lets be elegant here */
|
||||
switch (messagereceived.op) {
|
||||
case LIST_NOTES:
|
||||
unicastNoteList(conn, subject);
|
||||
unicastNoteList(conn, subject, userAndRoles);
|
||||
break;
|
||||
case RELOAD_NOTES_FROM_REPO:
|
||||
broadcastReloadedNoteList(subject);
|
||||
broadcastReloadedNoteList(subject, userAndRoles);
|
||||
break;
|
||||
case GET_HOME_NOTE:
|
||||
sendHomeNote(conn, userAndRoles, notebook, messagereceived);
|
||||
|
|
@ -230,8 +235,8 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
case LIST_CONFIGURATIONS:
|
||||
sendAllConfigurations(conn, userAndRoles, notebook);
|
||||
break;
|
||||
case CHECKPOINT_NOTEBOOK:
|
||||
checkpointNotebook(conn, notebook, messagereceived);
|
||||
case CHECKPOINT_NOTE:
|
||||
checkpointNote(conn, notebook, messagereceived);
|
||||
break;
|
||||
case LIST_REVISION_HISTORY:
|
||||
listRevisionHistory(conn, notebook, messagereceived);
|
||||
|
|
@ -239,11 +244,11 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
case NOTE_REVISION:
|
||||
getNoteByRevision(conn, notebook, messagereceived);
|
||||
break;
|
||||
case LIST_NOTEBOOK_JOBS:
|
||||
unicastNotebookJobInfo(conn, messagereceived);
|
||||
case LIST_NOTE_JOBS:
|
||||
unicastNoteJobInfo(conn, messagereceived);
|
||||
break;
|
||||
case UNSUBSCRIBE_UPDATE_NOTEBOOK_JOBS:
|
||||
unsubscribeNotebookJobInfo(conn);
|
||||
case UNSUBSCRIBE_UPDATE_NOTE_JOBS:
|
||||
unsubscribeNoteJobInfo(conn);
|
||||
break;
|
||||
case GET_INTERPRETER_BINDINGS:
|
||||
getInterpreterBindings(conn, messagereceived);
|
||||
|
|
@ -268,6 +273,26 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
.getRemoteAddr(), conn.getRequest().getRemotePort(), code, reason);
|
||||
removeConnectionFromAllNote(conn);
|
||||
connectedSockets.remove(conn);
|
||||
removeUserConnection(conn.getUser(), conn);
|
||||
}
|
||||
|
||||
private void removeUserConnection(String user, NotebookSocket conn) {
|
||||
if (userConnectedSockets.containsKey(user)) {
|
||||
userConnectedSockets.get(user).remove(conn);
|
||||
} else {
|
||||
LOG.warn("Closing connection that is absent in user connections");
|
||||
}
|
||||
}
|
||||
|
||||
private void addUserConnection(String user, NotebookSocket conn) {
|
||||
conn.setUser(user);
|
||||
if (userConnectedSockets.containsKey(user)) {
|
||||
userConnectedSockets.get(user).add(conn);
|
||||
} else {
|
||||
Queue<NotebookSocket> socketQueue = new ConcurrentLinkedQueue<>();
|
||||
socketQueue.add(conn);
|
||||
userConnectedSockets.put(user, socketQueue);
|
||||
}
|
||||
}
|
||||
|
||||
protected Message deserializeMessage(String msg) {
|
||||
|
|
@ -383,8 +408,12 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
}
|
||||
|
||||
private void broadcastAll(Message m) {
|
||||
for (NotebookSocket conn : connectedSockets) {
|
||||
private void multicastToUser(String user, Message m) {
|
||||
if (!userConnectedSockets.containsKey(user)) {
|
||||
LOG.warn("Broadcasting to user that is not in connections map");
|
||||
return;
|
||||
}
|
||||
for (NotebookSocket conn: userConnectedSockets.get(user)) {
|
||||
try {
|
||||
conn.send(serializeMessage(m));
|
||||
} catch (IOException e) {
|
||||
|
|
@ -401,48 +430,49 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
}
|
||||
|
||||
public void unicastNotebookJobInfo(NotebookSocket conn, Message fromMessage) throws IOException {
|
||||
public void unicastNoteJobInfo(NotebookSocket conn, Message fromMessage) throws IOException {
|
||||
addConnectionToNote(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(), conn);
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
List<Map<String, Object>> notebookJobs = notebook()
|
||||
List<Map<String, Object>> noteJobs = notebook()
|
||||
.getJobListByUnixTime(false, 0, subject);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
response.put("jobs", noteJobs);
|
||||
|
||||
conn.send(serializeMessage(new Message(OP.LIST_NOTEBOOK_JOBS)
|
||||
.put("notebookJobs", response)));
|
||||
conn.send(serializeMessage(new Message(OP.LIST_NOTE_JOBS)
|
||||
.put("noteJobs", response)));
|
||||
}
|
||||
|
||||
public void broadcastUpdateNotebookJobInfo(long lastUpdateUnixTime) throws IOException {
|
||||
List<Map<String, Object>> notebookJobs = new LinkedList<>();
|
||||
public void broadcastUpdateNoteJobInfo(long lastUpdateUnixTime) throws IOException {
|
||||
List<Map<String, Object>> noteJobs = new LinkedList<>();
|
||||
Notebook notebookObject = notebook();
|
||||
List<Map<String, Object>> jobNotes = null;
|
||||
if (notebookObject != null) {
|
||||
jobNotes = notebook().getJobListByUnixTime(false, lastUpdateUnixTime, null);
|
||||
notebookJobs = jobNotes == null ? notebookJobs : jobNotes;
|
||||
noteJobs = jobNotes == null ? noteJobs : jobNotes;
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs != null ? notebookJobs : new LinkedList<>());
|
||||
response.put("jobs", noteJobs != null ? noteJobs : new LinkedList<>());
|
||||
|
||||
broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
}
|
||||
|
||||
public void unsubscribeNotebookJobInfo(NotebookSocket conn) {
|
||||
public void unsubscribeNoteJobInfo(NotebookSocket conn) {
|
||||
removeConnectionFromNote(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(), conn);
|
||||
}
|
||||
|
||||
public void saveInterpreterBindings(NotebookSocket conn, Message fromMessage) {
|
||||
String noteId = (String) fromMessage.data.get("noteID");
|
||||
String noteId = (String) fromMessage.data.get("noteId");
|
||||
try {
|
||||
List<String> settingIdList = gson.fromJson(String.valueOf(
|
||||
fromMessage.data.get("selectedSettingIds")), new TypeToken<ArrayList<String>>() {
|
||||
}.getType());
|
||||
notebook().bindInterpretersToNote(noteId, settingIdList);
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
notebook().bindInterpretersToNote(subject.getUser(), noteId, settingIdList);
|
||||
broadcastInterpreterBindings(noteId,
|
||||
InterpreterBindingUtils.getInterpreterBindings(notebook(), noteId));
|
||||
} catch (Exception e) {
|
||||
|
|
@ -452,20 +482,20 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
|
||||
public void getInterpreterBindings(NotebookSocket conn, Message fromMessage)
|
||||
throws IOException {
|
||||
String noteID = (String) fromMessage.data.get("noteID");
|
||||
String noteId = (String) fromMessage.data.get("noteId");
|
||||
List<InterpreterSettingsList> settingList =
|
||||
InterpreterBindingUtils.getInterpreterBindings(notebook(), noteID);
|
||||
InterpreterBindingUtils.getInterpreterBindings(notebook(), noteId);
|
||||
conn.send(serializeMessage(new Message(OP.INTERPRETER_BINDINGS)
|
||||
.put("interpreterBindings", settingList)));
|
||||
}
|
||||
|
||||
public List<Map<String, String>> generateNotebooksInfo(boolean needsReload,
|
||||
AuthenticationInfo subject) {
|
||||
public List<Map<String, String>> generateNotesInfo(boolean needsReload,
|
||||
AuthenticationInfo subject, HashSet<String> userAndRoles) {
|
||||
|
||||
Notebook notebook = notebook();
|
||||
|
||||
ZeppelinConfiguration conf = notebook.getConf();
|
||||
String homescreenNotebookId = conf.getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN);
|
||||
String homescreenNoteId = conf.getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN);
|
||||
boolean hideHomeScreenNotebookFromList = conf
|
||||
.getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE);
|
||||
|
||||
|
|
@ -476,12 +506,13 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
LOG.error("Fail to reload notes from repository", e);
|
||||
}
|
||||
}
|
||||
List<Note> notes = notebook.getAllNotes(subject);
|
||||
|
||||
List<Note> notes = notebook.getAllNotes(userAndRoles);
|
||||
List<Map<String, String>> notesInfo = new LinkedList<>();
|
||||
for (Note note : notes) {
|
||||
Map<String, String> info = new HashMap<>();
|
||||
|
||||
if (hideHomeScreenNotebookFromList && note.getId().equals(homescreenNotebookId)) {
|
||||
if (hideHomeScreenNotebookFromList && note.getId().equals(homescreenNoteId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -503,19 +534,45 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
.put("interpreterBindings", settingList));
|
||||
}
|
||||
|
||||
public void broadcastNoteList(AuthenticationInfo subject) {
|
||||
List<Map<String, String>> notesInfo = generateNotebooksInfo(false, subject);
|
||||
broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
public void broadcastNoteList(AuthenticationInfo subject, HashSet userAndRoles) {
|
||||
if (subject == null) {
|
||||
subject = new AuthenticationInfo(StringUtils.EMPTY);
|
||||
}
|
||||
//send first to requesting user
|
||||
List<Map<String, String>> notesInfo = generateNotesInfo(false, subject, userAndRoles);
|
||||
multicastToUser(subject.getUser(), new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
//to others afterwards
|
||||
for (String user: userConnectedSockets.keySet()) {
|
||||
if (subject.getUser() == user) {
|
||||
continue;
|
||||
}
|
||||
notesInfo = generateNotesInfo(false, new AuthenticationInfo(user), userAndRoles);
|
||||
multicastToUser(user, new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
}
|
||||
}
|
||||
|
||||
public void unicastNoteList(NotebookSocket conn, AuthenticationInfo subject) {
|
||||
List<Map<String, String>> notesInfo = generateNotebooksInfo(false, subject);
|
||||
public void unicastNoteList(NotebookSocket conn, AuthenticationInfo subject,
|
||||
HashSet<String> userAndRoles) {
|
||||
List<Map<String, String>> notesInfo = generateNotesInfo(false, subject, userAndRoles);
|
||||
unicast(new Message(OP.NOTES_INFO).put("notes", notesInfo), conn);
|
||||
}
|
||||
|
||||
public void broadcastReloadedNoteList(AuthenticationInfo subject) {
|
||||
List<Map<String, String>> notesInfo = generateNotebooksInfo(true, subject);
|
||||
broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
public void broadcastReloadedNoteList(AuthenticationInfo subject, HashSet userAndRoles) {
|
||||
if (subject == null) {
|
||||
subject = new AuthenticationInfo(StringUtils.EMPTY);
|
||||
}
|
||||
//reload and reply first to requesting user
|
||||
List<Map<String, String>> notesInfo = generateNotesInfo(true, subject, userAndRoles);
|
||||
multicastToUser(subject.getUser(), new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
//to others afterwards
|
||||
for (String user: userConnectedSockets.keySet()) {
|
||||
if (subject.getUser() == user) {
|
||||
continue;
|
||||
}
|
||||
//reloaded already above; parameter - false
|
||||
notesInfo = generateNotesInfo(false, new AuthenticationInfo(user), userAndRoles);
|
||||
multicastToUser(user, new Message(OP.NOTES_INFO).put("notes", notesInfo));
|
||||
}
|
||||
}
|
||||
|
||||
void permissionError(NotebookSocket conn, String op,
|
||||
|
|
@ -544,6 +601,8 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
return;
|
||||
}
|
||||
|
||||
String user = fromMessage.principal;
|
||||
|
||||
Note note = notebook.getNote(noteId);
|
||||
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
if (note != null) {
|
||||
|
|
@ -554,7 +613,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
addConnectionToNote(note.getId(), conn);
|
||||
conn.send(serializeMessage(new Message(OP.NOTE).put("note", note)));
|
||||
sendAllAngularObjects(note, conn);
|
||||
sendAllAngularObjects(note, user, conn);
|
||||
} else {
|
||||
conn.send(serializeMessage(new Message(OP.NOTE).put("note", null)));
|
||||
}
|
||||
|
|
@ -563,6 +622,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
private void sendHomeNote(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
Notebook notebook, Message fromMessage) throws IOException {
|
||||
String noteId = notebook.getConf().getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN);
|
||||
String user = fromMessage.principal;
|
||||
|
||||
Note note = null;
|
||||
if (noteId != null) {
|
||||
|
|
@ -578,7 +638,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
addConnectionToNote(note.getId(), conn);
|
||||
conn.send(serializeMessage(new Message(OP.NOTE).put("note", note)));
|
||||
sendAllAngularObjects(note, conn);
|
||||
sendAllAngularObjects(note, user, conn);
|
||||
} else {
|
||||
removeConnectionFromAllNote(conn);
|
||||
conn.send(serializeMessage(new Message(OP.NOTE).put("note", null)));
|
||||
|
|
@ -618,7 +678,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
note.persist(subject);
|
||||
broadcastNote(note);
|
||||
broadcastNoteList(subject);
|
||||
broadcastNoteList(subject, userAndRoles);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +713,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
note.persist(subject);
|
||||
addConnectionToNote(note.getId(), (NotebookSocket) conn);
|
||||
conn.send(serializeMessage(new Message(OP.NEW_NOTE).put("note", note)));
|
||||
broadcastNoteList(subject);
|
||||
broadcastNoteList(subject, userAndRoles);
|
||||
}
|
||||
|
||||
private void removeNote(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
|
|
@ -675,7 +735,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
notebook.removeNote(noteId, subject);
|
||||
removeNote(noteId);
|
||||
broadcastNoteList(subject);
|
||||
broadcastNoteList(subject, userAndRoles);
|
||||
}
|
||||
|
||||
private void updateParagraph(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
|
|
@ -717,7 +777,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
addConnectionToNote(newNote.getId(), (NotebookSocket) conn);
|
||||
conn.send(serializeMessage(new Message(OP.NEW_NOTE).put("note", newNote)));
|
||||
broadcastNoteList(subject);
|
||||
broadcastNoteList(subject, userAndRoles);
|
||||
}
|
||||
|
||||
protected Note importNote(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
|
|
@ -725,16 +785,18 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
throws IOException {
|
||||
Note note = null;
|
||||
if (fromMessage != null) {
|
||||
String noteName = (String) ((Map) fromMessage.get("notebook")).get("name");
|
||||
String noteJson = gson.toJson(fromMessage.get("notebook"));
|
||||
String noteName = (String) ((Map) fromMessage.get("note")).get("name");
|
||||
String noteJson = gson.toJson(fromMessage.get("note"));
|
||||
AuthenticationInfo subject = null;
|
||||
if (fromMessage.principal != null) {
|
||||
subject = new AuthenticationInfo(fromMessage.principal);
|
||||
} else {
|
||||
subject = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
note = notebook.importNote(noteJson, noteName, subject);
|
||||
note.persist(subject);
|
||||
broadcastNote(note);
|
||||
broadcastNoteList(subject);
|
||||
broadcastNoteList(subject, userAndRoles);
|
||||
}
|
||||
return note;
|
||||
}
|
||||
|
|
@ -748,7 +810,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
String noteId = getOpenNoteId(conn);
|
||||
final Note note = notebook.getNote(noteId);
|
||||
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
|
||||
permissionError(conn, "write", fromMessage.principal,
|
||||
userAndRoles, notebookAuthorization.getWriters(noteId));
|
||||
|
|
@ -757,7 +819,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
|
||||
/** We dont want to remove the last paragraph */
|
||||
if (!note.isLastParagraph(paragraphId)) {
|
||||
note.removeParagraph(paragraphId);
|
||||
note.removeParagraph(subject.getUser(), paragraphId);
|
||||
note.persist(subject);
|
||||
broadcastNote(note);
|
||||
}
|
||||
|
|
@ -813,6 +875,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
String interpreterGroupId = (String) fromMessage.get("interpreterGroupId");
|
||||
String varName = (String) fromMessage.get("name");
|
||||
Object varValue = fromMessage.get("value");
|
||||
String user = fromMessage.principal;
|
||||
AngularObject ao = null;
|
||||
boolean global = false;
|
||||
// propagate change to (Remote) AngularObjectRegistry
|
||||
|
|
@ -821,12 +884,12 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
List<InterpreterSetting> settings = notebook.getInterpreterFactory()
|
||||
.getInterpreterSettings(note.getId());
|
||||
for (InterpreterSetting setting : settings) {
|
||||
if (setting.getInterpreterGroup(note.getId()) == null) {
|
||||
if (setting.getInterpreterGroup(user, note.getId()) == null) {
|
||||
continue;
|
||||
}
|
||||
if (interpreterGroupId.equals(setting.getInterpreterGroup(note.getId()).getId())) {
|
||||
if (interpreterGroupId.equals(setting.getInterpreterGroup(user, note.getId()).getId())) {
|
||||
AngularObjectRegistry angularObjectRegistry = setting
|
||||
.getInterpreterGroup(note.getId()).getAngularObjectRegistry();
|
||||
.getInterpreterGroup(user, note.getId()).getAngularObjectRegistry();
|
||||
|
||||
// first trying to get local registry
|
||||
ao = angularObjectRegistry.get(varName, noteId, paragraphId);
|
||||
|
|
@ -863,12 +926,12 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
List<InterpreterSetting> settings = notebook.getInterpreterFactory()
|
||||
.getInterpreterSettings(note.getId());
|
||||
for (InterpreterSetting setting : settings) {
|
||||
if (setting.getInterpreterGroup(n.getId()) == null) {
|
||||
if (setting.getInterpreterGroup(user, n.getId()) == null) {
|
||||
continue;
|
||||
}
|
||||
if (interpreterGroupId.equals(setting.getInterpreterGroup(n.getId()).getId())) {
|
||||
if (interpreterGroupId.equals(setting.getInterpreterGroup(user, n.getId()).getId())) {
|
||||
AngularObjectRegistry angularObjectRegistry = setting
|
||||
.getInterpreterGroup(n.getId()).getAngularObjectRegistry();
|
||||
.getInterpreterGroup(user, n.getId()).getAngularObjectRegistry();
|
||||
this.broadcastExcept(
|
||||
n.getId(),
|
||||
new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
|
||||
|
|
@ -1033,12 +1096,12 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
final AngularObject removed = registry.remove(varName, noteId, paragraphId);
|
||||
if (removed != null) {
|
||||
this.broadcastExcept(
|
||||
noteId,
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", removed)
|
||||
.put("interpreterGroupId", interpreterGroupId)
|
||||
.put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId),
|
||||
conn);
|
||||
noteId,
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", removed)
|
||||
.put("interpreterGroupId", interpreterGroupId)
|
||||
.put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId),
|
||||
conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1054,7 +1117,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
String noteId = getOpenNoteId(conn);
|
||||
final Note note = notebook.getNote(noteId);
|
||||
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
|
||||
permissionError(conn, "write", fromMessage.principal,
|
||||
userAndRoles, notebookAuthorization.getWriters(noteId));
|
||||
|
|
@ -1069,11 +1132,11 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
private void insertParagraph(NotebookSocket conn, HashSet<String> userAndRoles,
|
||||
Notebook notebook, Message fromMessage) throws IOException {
|
||||
final int index = (int) Double.parseDouble(fromMessage.get("index")
|
||||
.toString());
|
||||
.toString());
|
||||
String noteId = getOpenNoteId(conn);
|
||||
final Note note = notebook.getNote(noteId);
|
||||
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
|
||||
permissionError(conn, "write", fromMessage.principal,
|
||||
userAndRoles, notebookAuthorization.getWriters(noteId));
|
||||
|
|
@ -1125,14 +1188,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
String text = (String) fromMessage.get("paragraph");
|
||||
p.setText(text);
|
||||
p.setTitle((String) fromMessage.get("title"));
|
||||
if (!fromMessage.principal.equals("anonymous")) {
|
||||
AuthenticationInfo authenticationInfo = new AuthenticationInfo(fromMessage.principal,
|
||||
fromMessage.ticket);
|
||||
p.setAuthenticationInfo(authenticationInfo);
|
||||
|
||||
} else {
|
||||
p.setAuthenticationInfo(new AuthenticationInfo());
|
||||
}
|
||||
AuthenticationInfo authenticationInfo =
|
||||
new AuthenticationInfo(fromMessage.principal, fromMessage.ticket);
|
||||
p.setAuthenticationInfo(authenticationInfo);
|
||||
|
||||
Map<String, Object> params = (Map<String, Object>) fromMessage
|
||||
.get("params");
|
||||
|
|
@ -1194,7 +1252,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
.put("configurations", configurations)));
|
||||
}
|
||||
|
||||
private void checkpointNotebook(NotebookSocket conn, Notebook notebook,
|
||||
private void checkpointNote(NotebookSocket conn, Notebook notebook,
|
||||
Message fromMessage) throws IOException {
|
||||
String noteId = (String) fromMessage.get("noteId");
|
||||
String commitMessage = (String) fromMessage.get("commitMessage");
|
||||
|
|
@ -1326,7 +1384,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
@Override
|
||||
public void onParagraphRemove(Paragraph p) {
|
||||
try {
|
||||
notebookServer.broadcastUpdateNotebookJobInfo(System.currentTimeMillis() - 5000);
|
||||
notebookServer.broadcastUpdateNoteJobInfo(System.currentTimeMillis() - 5000);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("can not broadcast for job manager {}", ioe.getMessage());
|
||||
}
|
||||
|
|
@ -1335,14 +1393,14 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
@Override
|
||||
public void onNoteRemove(Note note) {
|
||||
try {
|
||||
notebookServer.broadcastUpdateNotebookJobInfo(System.currentTimeMillis() - 5000);
|
||||
notebookServer.broadcastUpdateNoteJobInfo(System.currentTimeMillis() - 5000);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("can not broadcast for job manager {}", ioe.getMessage());
|
||||
}
|
||||
|
||||
List<Map<String, Object>> notesInfo = new LinkedList<>();
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("notebookId", note.getId());
|
||||
info.put("noteId", note.getId());
|
||||
// set paragraphs
|
||||
List<Map<String, Object>> paragraphsInfo = new LinkedList<>();
|
||||
|
||||
|
|
@ -1358,7 +1416,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
response.put("jobs", notesInfo);
|
||||
|
||||
notebookServer.broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1366,35 +1424,35 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
public void onParagraphCreate(Paragraph p) {
|
||||
Notebook notebook = notebookServer.notebook();
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListByParagraphId(
|
||||
p.getId()
|
||||
p.getId()
|
||||
);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
|
||||
notebookServer.broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoteCreate(Note note) {
|
||||
Notebook notebook = notebookServer.notebook();
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListBymNotebookId(
|
||||
note.getId()
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListByNoteId(
|
||||
note.getId()
|
||||
);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
|
||||
notebookServer.broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParagraphStatusChange(Paragraph p, Status status) {
|
||||
Notebook notebook = notebookServer.notebook();
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListByParagraphId(
|
||||
p.getId()
|
||||
p.getId()
|
||||
);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
|
@ -1402,21 +1460,21 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
response.put("jobs", notebookJobs);
|
||||
|
||||
notebookServer.broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnbindInterpreter(Note note, InterpreterSetting setting) {
|
||||
Notebook notebook = notebookServer.notebook();
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListBymNotebookId(
|
||||
note.getId()
|
||||
List<Map<String, Object>> notebookJobs = notebook.getJobListByNoteId(
|
||||
note.getId()
|
||||
);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("lastResponseUnixTime", System.currentTimeMillis());
|
||||
response.put("jobs", notebookJobs);
|
||||
|
||||
notebookServer.broadcast(JOB_MANAGER_SERVICE.JOB_MANAGER_PAGE.getKey(),
|
||||
new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS).put("notebookRunningJobs", response));
|
||||
new Message(OP.LIST_UPDATE_NOTE_JOBS).put("noteRunningJobs", response));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1457,7 +1515,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
LOG.info("Job {} is finished", job.getId());
|
||||
try {
|
||||
//TODO(khalid): may change interface for JobListener and pass subject from interpreter
|
||||
note.persist(null);
|
||||
note.persist(job instanceof Paragraph ? ((Paragraph) job).getAuthenticationInfo() : null);
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.toString(), e);
|
||||
}
|
||||
|
|
@ -1465,7 +1523,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
notebookServer.broadcastNote(note);
|
||||
|
||||
try {
|
||||
notebookServer.broadcastUpdateNotebookJobInfo(System.currentTimeMillis() - 5000);
|
||||
notebookServer.broadcastUpdateNoteJobInfo(System.currentTimeMillis() - 5000);
|
||||
} catch (IOException e) {
|
||||
LOG.error("can not broadcast for job manager {}", e);
|
||||
}
|
||||
|
|
@ -1480,9 +1538,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
@Override
|
||||
public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
|
||||
Message msg = new Message(OP.PARAGRAPH_APPEND_OUTPUT)
|
||||
.put("noteId", paragraph.getNote().getId())
|
||||
.put("paragraphId", paragraph.getId())
|
||||
.put("data", output);
|
||||
.put("noteId", paragraph.getNote().getId())
|
||||
.put("paragraphId", paragraph.getId())
|
||||
.put("data", output);
|
||||
|
||||
notebookServer.broadcast(paragraph.getNote().getId(), msg);
|
||||
}
|
||||
|
|
@ -1496,9 +1554,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
@Override
|
||||
public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
|
||||
Message msg = new Message(OP.PARAGRAPH_UPDATE_OUTPUT)
|
||||
.put("noteId", paragraph.getNote().getId())
|
||||
.put("paragraphId", paragraph.getId())
|
||||
.put("data", output);
|
||||
.put("noteId", paragraph.getNote().getId())
|
||||
.put("paragraphId", paragraph.getId())
|
||||
.put("data", output);
|
||||
|
||||
notebookServer.broadcast(paragraph.getNote().getId(), msg);
|
||||
}
|
||||
|
|
@ -1513,7 +1571,8 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
return new NotebookInformationListener(this);
|
||||
}
|
||||
|
||||
private void sendAllAngularObjects(Note note, NotebookSocket conn) throws IOException {
|
||||
private void sendAllAngularObjects(Note note, String user, NotebookSocket conn)
|
||||
throws IOException {
|
||||
List<InterpreterSetting> settings =
|
||||
notebook().getInterpreterFactory().getInterpreterSettings(note.getId());
|
||||
if (settings == null || settings.size() == 0) {
|
||||
|
|
@ -1521,17 +1580,15 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
|
||||
for (InterpreterSetting intpSetting : settings) {
|
||||
AngularObjectRegistry registry = intpSetting.getInterpreterGroup(note.getId())
|
||||
.getAngularObjectRegistry();
|
||||
AngularObjectRegistry registry =
|
||||
intpSetting.getInterpreterGroup(user, note.getId()).getAngularObjectRegistry();
|
||||
List<AngularObject> objects = registry.getAllWithGlobal(note.getId());
|
||||
for (AngularObject object : objects) {
|
||||
conn.send(serializeMessage(new Message(OP.ANGULAR_OBJECT_UPDATE)
|
||||
.put("angularObject", object)
|
||||
.put("interpreterGroupId",
|
||||
intpSetting.getInterpreterGroup(note.getId()).getId())
|
||||
.put("noteId", note.getId())
|
||||
.put("paragraphId", object.getParagraphId())
|
||||
));
|
||||
conn.send(serializeMessage(
|
||||
new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", object)
|
||||
.put("interpreterGroupId",
|
||||
intpSetting.getInterpreterGroup(user, note.getId()).getId())
|
||||
.put("noteId", note.getId()).put("paragraphId", object.getParagraphId())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1598,9 +1655,10 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
String paragraphId = (String) fromMessage.get("paragraphId");
|
||||
String replName = (String) fromMessage.get("magic");
|
||||
String noteId = getOpenNoteId(conn);
|
||||
String user = fromMessage.principal;
|
||||
Message resp = new Message(OP.EDITOR_SETTING);
|
||||
resp.put("paragraphId", paragraphId);
|
||||
resp.put("editor", notebook().getInterpreterFactory().getEditorSetting(noteId, replName));
|
||||
resp.put("editor", notebook().getInterpreterFactory().getEditorSetting(user, noteId, replName));
|
||||
conn.send(serializeMessage(resp));
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
|
||||
|
|
@ -32,12 +33,14 @@ public class NotebookSocket extends WebSocketAdapter {
|
|||
private NotebookSocketListener listener;
|
||||
private HttpServletRequest request;
|
||||
private String protocol;
|
||||
private String user;
|
||||
|
||||
public NotebookSocket(HttpServletRequest req, String protocol,
|
||||
NotebookSocketListener listener) {
|
||||
this.listener = listener;
|
||||
this.request = req;
|
||||
this.protocol = protocol;
|
||||
this.user = StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -69,4 +72,11 @@ public class NotebookSocket extends WebSocketAdapter {
|
|||
connection.getRemote().sendString(serializeMessage);
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,4 +119,10 @@ public class SecurityUtils {
|
|||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checked if shiro enabled or not
|
||||
*/
|
||||
public static boolean isAuthenticated() {
|
||||
return org.apache.shiro.SecurityUtils.getSubject().isAuthenticated();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
zeppelin-server/src/main/resources/log4j.properties
Normal file
25
zeppelin-server/src/main/resources/log4j.properties
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You 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.
|
||||
#
|
||||
|
||||
log4j.rootLogger = INFO, stdout
|
||||
|
||||
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%5p [%d] ({%t} %F[%M]:%L) - %m%n
|
||||
|
||||
log4j.additivity.org.apache.zeppelin.interpreter = false
|
||||
log4j.logger.org.apache.zeppelin.interpreter = DEBUG, stdout
|
||||
|
|
@ -18,14 +18,19 @@
|
|||
[users]
|
||||
# List of users with their password allowed to access Zeppelin.
|
||||
# To use a different strategy (LDAP / Database / ...) check the shiro doc at http://shiro.apache.org/configuration.html#Configuration-INISections
|
||||
admin = password
|
||||
admin = password, admin
|
||||
user1 = user1, role1
|
||||
|
||||
|
||||
[urls]
|
||||
|
||||
# anon means the access is anonymous.
|
||||
# authcBasic means Basic Auth Security
|
||||
# To enfore security, comment the line below and uncomment the next one
|
||||
/** = anon
|
||||
#/** = authcBasic
|
||||
#/** = authc
|
||||
|
||||
[roles]
|
||||
role1 = *
|
||||
role2 = *
|
||||
role3 = *
|
||||
admin = *
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ abstract public class AbstractZeppelinIT {
|
|||
driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']"))
|
||||
.sendKeys(Keys.ENTER);
|
||||
ZeppelinITUtils.sleep(1000, true);
|
||||
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this notebook')]" +
|
||||
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this note')]" +
|
||||
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();
|
||||
ZeppelinITUtils.sleep(100, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ public class AuthenticationIT extends AbstractZeppelinIT {
|
|||
try {
|
||||
WebElement element = pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"),
|
||||
MAX_BROWSER_TIMEOUT_SEC);
|
||||
collector.checkThat("Check is user has permission to view this notebook link", false,
|
||||
collector.checkThat("Check is user has permission to view this note link", false,
|
||||
CoreMatchers.equalTo(element.isDisplayed()));
|
||||
} catch (Exception e) {
|
||||
//This should have failed, nothing to worry.
|
||||
|
|
@ -191,7 +191,7 @@ public class AuthenticationIT extends AbstractZeppelinIT {
|
|||
List<WebElement> privilegesModal = driver.findElements(
|
||||
By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" +
|
||||
"//div[contains(.,'Insufficient privileges')]"));
|
||||
collector.checkThat("Check is user has permission to view this notebook", 1,
|
||||
collector.checkThat("Check is user has permission to view this note", 1,
|
||||
CoreMatchers.equalTo(privilegesModal.size()));
|
||||
driver.findElement(
|
||||
By.xpath("//div[@class='modal-content'][contains(.,'Insufficient privileges')]" +
|
||||
|
|
@ -202,7 +202,7 @@ public class AuthenticationIT extends AbstractZeppelinIT {
|
|||
try {
|
||||
WebElement element = pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"),
|
||||
MAX_BROWSER_TIMEOUT_SEC);
|
||||
collector.checkThat("Check is user has permission to view this notebook link", false,
|
||||
collector.checkThat("Check is user has permission to view this note link", true,
|
||||
CoreMatchers.equalTo(element.isDisplayed()));
|
||||
} catch (Exception e) {
|
||||
//This should have failed, nothing to worry.
|
||||
|
|
@ -213,7 +213,7 @@ public class AuthenticationIT extends AbstractZeppelinIT {
|
|||
privilegesModal = driver.findElements(
|
||||
By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" +
|
||||
"//div[contains(.,'Insufficient privileges')]"));
|
||||
collector.checkThat("Check is user has permission to view this notebook", 0,
|
||||
collector.checkThat("Check is user has permission to view this note", 0,
|
||||
CoreMatchers.equalTo(privilegesModal.size()));
|
||||
deleteTestNotebook(driver);
|
||||
authenticationIT.logoutUser("finance2");
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']"))
|
||||
.sendKeys(Keys.ENTER);
|
||||
ZeppelinITUtils.sleep(1000, true);
|
||||
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this notebook')]" +
|
||||
driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this note')]" +
|
||||
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();
|
||||
ZeppelinITUtils.sleep(100, true);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ import org.apache.zeppelin.notebook.Note;
|
|||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
|
|
@ -47,6 +49,7 @@ import static org.junit.Assert.*;
|
|||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class InterpreterRestApiTest extends AbstractTestRestApi {
|
||||
Gson gson = new Gson();
|
||||
AuthenticationInfo anonymous;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
|
|
@ -58,6 +61,11 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
AbstractTestRestApi.shutDown();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
anonymous = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableInterpreters() throws IOException {
|
||||
// when
|
||||
|
|
@ -90,7 +98,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
String jsonRequest = "{\"name\":\"md2\",\"group\":\"md\",\"properties\":{\"propname\":\"propvalue\"}," +
|
||||
"\"interpreterGroup\":[{\"class\":\"org.apache.zeppelin.markdown.Markdown\",\"name\":\"md\"}]," +
|
||||
"\"dependencies\":[]," +
|
||||
"\"option\": { \"remote\": true, \"perNoteSession\": false }}";
|
||||
"\"option\": { \"remote\": true, \"session\": false }}";
|
||||
PostMethod post = httpPost("/interpreter/setting/", jsonRequest);
|
||||
LOG.info("testSettingCRUD create response\n" + post.getResponseBodyAsString());
|
||||
assertThat("test create method:", post, isCreated());
|
||||
|
|
@ -106,7 +114,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
jsonRequest = "{\"name\":\"md2\",\"group\":\"md\",\"properties\":{\"propname\":\"Otherpropvalue\"}," +
|
||||
"\"interpreterGroup\":[{\"class\":\"org.apache.zeppelin.markdown.Markdown\",\"name\":\"md\"}]," +
|
||||
"\"dependencies\":[]," +
|
||||
"\"option\": { \"remote\": true, \"perNoteSession\": false }}";
|
||||
"\"option\": { \"remote\": true, \"session\": false }}";
|
||||
PutMethod put = httpPut("/interpreter/setting/" + newSettingId, jsonRequest);
|
||||
LOG.info("testSettingCRUD update response\n" + put.getResponseBodyAsString());
|
||||
assertThat("test update method:", put, isAllowed());
|
||||
|
|
@ -131,7 +139,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
@Test
|
||||
public void testInterpreterAutoBinding() throws IOException {
|
||||
// create note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
// check interpreter is binded
|
||||
GetMethod get = httpGet("/notebook/interpreter/bind/" + note.getId());
|
||||
|
|
@ -144,13 +152,13 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
get.releaseConnection();
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterpreterRestart() throws IOException, InterruptedException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note.addParagraph();
|
||||
Paragraph p = note.getLastParagraph();
|
||||
Map config = p.getConfig();
|
||||
|
|
@ -159,6 +167,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
// run markdown paragraph
|
||||
p.setConfig(config);
|
||||
p.setText("%md markdown");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
while (p.getStatus() != Status.FINISHED) {
|
||||
Thread.sleep(100);
|
||||
|
|
@ -181,13 +190,14 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
p = note.addParagraph();
|
||||
p.setConfig(config);
|
||||
p.setText("%md markdown restarted");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
while (p.getStatus() != Status.FINISHED) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
assertEquals("<p>markdown restarted</p>\n", p.getResult().message());
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ import org.apache.zeppelin.notebook.Note;
|
|||
import org.apache.zeppelin.notebook.NotebookAuthorization;
|
||||
import org.apache.zeppelin.notebook.NotebookAuthorizationInfoSaving;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
|
|
@ -49,6 +51,7 @@ import static org.junit.Assert.assertThat;
|
|||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class NotebookRestApiTest extends AbstractTestRestApi {
|
||||
Gson gson = new Gson();
|
||||
AuthenticationInfo anonymous;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
|
|
@ -60,9 +63,14 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
AbstractTestRestApi.shutDown();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
anonymous = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPermissions() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(null);
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
// Set only readers
|
||||
String jsonRequest = "{\"readers\":[\"admin-team\"],\"owners\":[]," +
|
||||
"\"writers\":[]}";
|
||||
|
|
@ -85,7 +93,7 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
get.releaseConnection();
|
||||
|
||||
|
||||
Note note2 = ZeppelinServer.notebook.createNote(null);
|
||||
Note note2 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
// Set only writers
|
||||
jsonRequest = "{\"readers\":[],\"owners\":[]," +
|
||||
"\"writers\":[\"admin-team\"]}";
|
||||
|
|
@ -119,14 +127,14 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
assertEquals(authInfo.get("owners"), Lists.newArrayList());
|
||||
get.releaseConnection();
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note2.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), anonymous);
|
||||
ZeppelinServer.notebook.removeNote(note2.getId(), anonymous);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNoteParagraphJobStatus() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(null);
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note1.addParagraph();
|
||||
|
||||
String paragraphId = note1.getLastParagraph().getId();
|
||||
|
|
@ -142,33 +150,33 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
assertEquals(paragraphStatus.get("status"), "READY");
|
||||
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), anonymous);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloneNotebook() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(null);
|
||||
public void testCloneNote() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
PostMethod post = httpPost("/notebook/" + note1.getId(), "");
|
||||
LOG.info("testCloneNotebook response\n" + post.getResponseBodyAsString());
|
||||
LOG.info("testCloneNote response\n" + post.getResponseBodyAsString());
|
||||
assertThat(post, isCreated());
|
||||
Map<String, Object> resp = gson.fromJson(post.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
String clonedNotebookId = (String) resp.get("body");
|
||||
String clonedNoteId = (String) resp.get("body");
|
||||
post.releaseConnection();
|
||||
|
||||
GetMethod get = httpGet("/notebook/" + clonedNotebookId);
|
||||
GetMethod get = httpGet("/notebook/" + clonedNoteId);
|
||||
assertThat(get, isAllowed());
|
||||
Map<String, Object> resp2 = gson.fromJson(get.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
Map<String, Object> resp2Body = (Map<String, Object>) resp2.get("body");
|
||||
|
||||
assertEquals((String)resp2Body.get("name"), "Note " + clonedNotebookId);
|
||||
assertEquals((String)resp2Body.get("name"), "Note " + clonedNoteId);
|
||||
get.releaseConnection();
|
||||
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(clonedNotebookId, null);
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), anonymous);
|
||||
ZeppelinServer.notebook.removeNote(clonedNoteId, anonymous);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,20 +19,22 @@ package org.apache.zeppelin.rest;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.httpclient.methods.DeleteMethod;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.httpclient.methods.PutMethod;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting;
|
||||
import org.apache.zeppelin.notebook.Note;
|
||||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
|
|
@ -50,6 +52,7 @@ import static org.junit.Assert.*;
|
|||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
||||
Gson gson = new Gson();
|
||||
AuthenticationInfo anonymous;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
|
|
@ -61,6 +64,11 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
AbstractTestRestApi.shutDown();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
anonymous = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
|
||||
/***
|
||||
* ROOT API TEST
|
||||
***/
|
||||
|
|
@ -74,10 +82,10 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotebookInfo() throws IOException {
|
||||
LOG.info("testGetNotebookInfo");
|
||||
public void testGetNoteInfo() throws IOException {
|
||||
LOG.info("testGetNoteInfo");
|
||||
// Create note to get info
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -86,12 +94,12 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
paragraph.setConfig(config);
|
||||
String paragraphText = "%md This is my new paragraph in my new note";
|
||||
paragraph.setText(paragraphText);
|
||||
note.persist(null);
|
||||
note.persist(anonymous);
|
||||
|
||||
String sourceNoteID = note.getId();
|
||||
GetMethod get = httpGet("/notebook/" + sourceNoteID);
|
||||
LOG.info("testGetNotebookInfo \n" + get.getResponseBodyAsString());
|
||||
assertThat("test notebook get method:", get, isAllowed());
|
||||
String sourceNoteId = note.getId();
|
||||
GetMethod get = httpGet("/notebook/" + sourceNoteId);
|
||||
LOG.info("testGetNoteInfo \n" + get.getResponseBodyAsString());
|
||||
assertThat("test note get method:", get, isAllowed());
|
||||
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
|
|
@ -104,44 +112,46 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
assertTrue(paragraphs.size() > 0);
|
||||
assertEquals(paragraphText, paragraphs.get(0).get("text"));
|
||||
//
|
||||
ZeppelinServer.notebook.removeNote(sourceNoteId, anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotebookCreateWithName() throws IOException {
|
||||
public void testNoteCreateWithName() throws IOException {
|
||||
String noteName = "Test note name";
|
||||
testNotebookCreate(noteName);
|
||||
testNoteCreate(noteName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotebookCreateNoName() throws IOException {
|
||||
testNotebookCreate("");
|
||||
public void testNoteCreateNoName() throws IOException {
|
||||
testNoteCreate("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotebookCreateWithParagraphs() throws IOException {
|
||||
// Call Create Notebook REST API
|
||||
public void testNoteCreateWithParagraphs() throws IOException {
|
||||
// Call Create Note REST API
|
||||
String noteName = "test";
|
||||
String jsonRequest = "{\"name\":\"" + noteName + "\", \"paragraphs\": [" +
|
||||
"{\"title\": \"title1\", \"text\": \"text1\"}," +
|
||||
"{\"title\": \"title2\", \"text\": \"text2\"}" +
|
||||
"]}";
|
||||
PostMethod post = httpPost("/notebook/", jsonRequest);
|
||||
LOG.info("testNotebookCreate \n" + post.getResponseBodyAsString());
|
||||
assertThat("test notebook create method:", post, isCreated());
|
||||
LOG.info("testNoteCreate \n" + post.getResponseBodyAsString());
|
||||
assertThat("test note create method:", post, isCreated());
|
||||
|
||||
Map<String, Object> resp = gson.fromJson(post.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
|
||||
String newNotebookId = (String) resp.get("body");
|
||||
LOG.info("newNotebookId:=" + newNotebookId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNotebookId);
|
||||
String newNoteId = (String) resp.get("body");
|
||||
LOG.info("newNoteId:=" + newNoteId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNoteId);
|
||||
assertNotNull("Can not find new note by id", newNote);
|
||||
// This is partial test as newNote is in memory but is not persistent
|
||||
String newNoteName = newNote.getName();
|
||||
LOG.info("new note name is: " + newNoteName);
|
||||
String expectedNoteName = noteName;
|
||||
if (noteName.isEmpty()) {
|
||||
expectedNoteName = "Note " + newNotebookId;
|
||||
expectedNoteName = "Note " + newNoteId;
|
||||
}
|
||||
assertEquals("compare note name", expectedNoteName, newNoteName);
|
||||
assertEquals("initial paragraph check failed", 3, newNote.getParagraphs().size());
|
||||
|
|
@ -153,34 +163,34 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
assertTrue("paragraph text check failed", p.getText().startsWith("text"));
|
||||
}
|
||||
// cleanup
|
||||
ZeppelinServer.notebook.removeNote(newNotebookId, null);
|
||||
ZeppelinServer.notebook.removeNote(newNoteId, anonymous);
|
||||
post.releaseConnection();
|
||||
}
|
||||
|
||||
private void testNotebookCreate(String noteName) throws IOException {
|
||||
// Call Create Notebook REST API
|
||||
private void testNoteCreate(String noteName) throws IOException {
|
||||
// Call Create Note REST API
|
||||
String jsonRequest = "{\"name\":\"" + noteName + "\"}";
|
||||
PostMethod post = httpPost("/notebook/", jsonRequest);
|
||||
LOG.info("testNotebookCreate \n" + post.getResponseBodyAsString());
|
||||
assertThat("test notebook create method:", post, isCreated());
|
||||
LOG.info("testNoteCreate \n" + post.getResponseBodyAsString());
|
||||
assertThat("test note create method:", post, isCreated());
|
||||
|
||||
Map<String, Object> resp = gson.fromJson(post.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
|
||||
String newNotebookId = (String) resp.get("body");
|
||||
LOG.info("newNotebookId:=" + newNotebookId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNotebookId);
|
||||
String newNoteId = (String) resp.get("body");
|
||||
LOG.info("newNoteId:=" + newNoteId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNoteId);
|
||||
assertNotNull("Can not find new note by id", newNote);
|
||||
// This is partial test as newNote is in memory but is not persistent
|
||||
String newNoteName = newNote.getName();
|
||||
LOG.info("new note name is: " + newNoteName);
|
||||
String expectedNoteName = noteName;
|
||||
if (noteName.isEmpty()) {
|
||||
expectedNoteName = "Note " + newNotebookId;
|
||||
expectedNoteName = "Note " + newNoteId;
|
||||
}
|
||||
assertEquals("compare note name", expectedNoteName, newNoteName);
|
||||
// cleanup
|
||||
ZeppelinServer.notebook.removeNote(newNotebookId, null);
|
||||
ZeppelinServer.notebook.removeNote(newNoteId, anonymous);
|
||||
post.releaseConnection();
|
||||
|
||||
}
|
||||
|
|
@ -189,23 +199,23 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
public void testDeleteNote() throws IOException {
|
||||
LOG.info("testDeleteNote");
|
||||
//Create note and get ID
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
String noteId = note.getId();
|
||||
testDeleteNotebook(noteId);
|
||||
testDeleteNote(noteId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteNoteBadId() throws IOException {
|
||||
LOG.info("testDeleteNoteBadId");
|
||||
testDeleteNotebook("2AZFXEX97");
|
||||
testDeleteNotebook("bad_ID");
|
||||
testDeleteNote("2AZFXEX97");
|
||||
testDeleteNote("bad_ID");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExportNotebook() throws IOException {
|
||||
LOG.info("testExportNotebook");
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
public void testexportNote() throws IOException {
|
||||
LOG.info("testexportNote");
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("source note for export");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -213,12 +223,12 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
paragraph.setText("%md This is my new paragraph in my new note");
|
||||
note.persist(null);
|
||||
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());
|
||||
note.persist(anonymous);
|
||||
String sourceNoteId = note.getId();
|
||||
// Call export Note REST API
|
||||
GetMethod get = httpGet("/notebook/export/" + sourceNoteId);
|
||||
LOG.info("testNoteExport \n" + get.getResponseBodyAsString());
|
||||
assertThat("test note export method:", get, isAllowed());
|
||||
|
||||
Map<String, Object> resp =
|
||||
gson.fromJson(get.getResponseBodyAsString(),
|
||||
|
|
@ -227,7 +237,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
String exportJSON = (String) resp.get("body");
|
||||
assertNotNull("Can not find new notejson", exportJSON);
|
||||
LOG.info("export JSON:=" + exportJSON);
|
||||
ZeppelinServer.notebook.removeNote(sourceNoteID, null);
|
||||
ZeppelinServer.notebook.removeNote(sourceNoteId, anonymous);
|
||||
get.releaseConnection();
|
||||
|
||||
}
|
||||
|
|
@ -236,9 +246,9 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
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(null);
|
||||
LOG.info("testImortNote");
|
||||
// create test note
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName(noteName);
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -246,11 +256,11 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
paragraph.setText("%md This is my new paragraph in my new note");
|
||||
note.persist(null);
|
||||
String sourceNoteID = note.getId();
|
||||
note.persist(anonymous);
|
||||
String sourceNoteId = note.getId();
|
||||
// get note content as JSON
|
||||
String oldJson = getNoteContent(sourceNoteID);
|
||||
// call notebook post
|
||||
String oldJson = getNoteContent(sourceNoteId);
|
||||
// call note post
|
||||
PostMethod importPost = httpPost("/notebook/import/", oldJson);
|
||||
assertThat(importPost, isCreated());
|
||||
resp =
|
||||
|
|
@ -258,14 +268,14 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
new TypeToken<Map<String, Object>>() {}.getType());
|
||||
String importId = (String) resp.get("body");
|
||||
|
||||
assertNotNull("Did not get back a notebook id in body", importId);
|
||||
assertNotNull("Did not get back a note 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(), null);
|
||||
ZeppelinServer.notebook.removeNote(newNote.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
ZeppelinServer.notebook.removeNote(newNote.getId(), anonymous);
|
||||
importPost.releaseConnection();
|
||||
}
|
||||
|
||||
|
|
@ -283,24 +293,24 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
return body;
|
||||
}
|
||||
|
||||
private void testDeleteNotebook(String notebookId) throws IOException {
|
||||
private void testDeleteNote(String noteId) throws IOException {
|
||||
|
||||
DeleteMethod delete = httpDelete(("/notebook/" + notebookId));
|
||||
LOG.info("testDeleteNotebook delete response\n" + delete.getResponseBodyAsString());
|
||||
DeleteMethod delete = httpDelete(("/notebook/" + noteId));
|
||||
LOG.info("testDeleteNote delete response\n" + delete.getResponseBodyAsString());
|
||||
assertThat("Test delete method:", delete, isAllowed());
|
||||
delete.releaseConnection();
|
||||
// make sure note is deleted
|
||||
if (!notebookId.isEmpty()) {
|
||||
Note deletedNote = ZeppelinServer.notebook.getNote(notebookId);
|
||||
if (!noteId.isEmpty()) {
|
||||
Note deletedNote = ZeppelinServer.notebook.getNote(noteId);
|
||||
assertNull("Deleted note should be null", deletedNote);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloneNotebook() throws IOException, CloneNotSupportedException, IllegalArgumentException {
|
||||
LOG.info("testCloneNotebook");
|
||||
public void testCloneNote() throws IOException, CloneNotSupportedException, IllegalArgumentException {
|
||||
LOG.info("testCloneNote");
|
||||
// Create note to clone
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("source note for clone");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -308,40 +318,42 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
paragraph.setText("%md This is my new paragraph in my new note");
|
||||
note.persist(null);
|
||||
String sourceNoteID = note.getId();
|
||||
note.persist(anonymous);
|
||||
String sourceNoteId = note.getId();
|
||||
|
||||
String noteName = "clone Note Name";
|
||||
// Call Clone Notebook REST API
|
||||
// Call Clone Note REST API
|
||||
String jsonRequest = "{\"name\":\"" + noteName + "\"}";
|
||||
PostMethod post = httpPost("/notebook/" + sourceNoteID, jsonRequest);
|
||||
LOG.info("testNotebookClone \n" + post.getResponseBodyAsString());
|
||||
assertThat("test notebook clone method:", post, isCreated());
|
||||
PostMethod post = httpPost("/notebook/" + sourceNoteId, jsonRequest);
|
||||
LOG.info("testNoteClone \n" + post.getResponseBodyAsString());
|
||||
assertThat("test note clone method:", post, isCreated());
|
||||
|
||||
Map<String, Object> resp = gson.fromJson(post.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
|
||||
String newNotebookId = (String) resp.get("body");
|
||||
LOG.info("newNotebookId:=" + newNotebookId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNotebookId);
|
||||
String newNoteId = (String) resp.get("body");
|
||||
LOG.info("newNoteId:=" + newNoteId);
|
||||
Note newNote = ZeppelinServer.notebook.getNote(newNoteId);
|
||||
assertNotNull("Can not find new note by id", newNote);
|
||||
assertEquals("Compare note names", noteName, newNote.getName());
|
||||
assertEquals("Compare paragraphs count", note.getParagraphs().size(), newNote.getParagraphs().size());
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(newNote.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
ZeppelinServer.notebook.removeNote(newNote.getId(), anonymous);
|
||||
post.releaseConnection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListNotebooks() throws IOException {
|
||||
LOG.info("testListNotebooks");
|
||||
public void testListNotes() throws IOException {
|
||||
LOG.info("testListNotes");
|
||||
GetMethod get = httpGet("/notebook/ ");
|
||||
assertThat("List notebooks method", get, isAllowed());
|
||||
assertThat("List notes method", get, isAllowed());
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
List<Map<String, String>> body = (List<Map<String, String>>) resp.get("body");
|
||||
assertEquals("List notebooks are equal", ZeppelinServer.notebook.getAllNotes().size(), body.size());
|
||||
//TODO(khalid): anonymous or specific user notes?
|
||||
HashSet<String> anonymous = Sets.newHashSet("anonymous");
|
||||
assertEquals("List notes are equal", ZeppelinServer.notebook.getAllNotes(anonymous).size(), body.size());
|
||||
get.releaseConnection();
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +361,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
public void testNoteJobs() throws IOException, InterruptedException {
|
||||
LOG.info("testNoteJobs");
|
||||
// Create note to run test.
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -359,8 +371,8 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
paragraph.setConfig(config);
|
||||
|
||||
paragraph.setText("%md This is test paragraph.");
|
||||
note.persist(null);
|
||||
String noteID = note.getId();
|
||||
note.persist(anonymous);
|
||||
String noteId = note.getId();
|
||||
|
||||
note.runAll();
|
||||
// wait until job is finished or timeout.
|
||||
|
|
@ -373,38 +385,38 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
}
|
||||
|
||||
// Call Run Notebook Jobs REST API
|
||||
PostMethod postNoteJobs = httpPost("/notebook/job/" + noteID, "");
|
||||
assertThat("test notebook jobs run:", postNoteJobs, isAllowed());
|
||||
// Call Run note jobs REST API
|
||||
PostMethod postNoteJobs = httpPost("/notebook/job/" + noteId, "");
|
||||
assertThat("test note jobs run:", postNoteJobs, isAllowed());
|
||||
postNoteJobs.releaseConnection();
|
||||
|
||||
// Call Stop Notebook Jobs REST API
|
||||
DeleteMethod deleteNoteJobs = httpDelete("/notebook/job/" + noteID);
|
||||
assertThat("test notebook stop:", deleteNoteJobs, isAllowed());
|
||||
// Call Stop note jobs REST API
|
||||
DeleteMethod deleteNoteJobs = httpDelete("/notebook/job/" + noteId);
|
||||
assertThat("test note stop:", deleteNoteJobs, isAllowed());
|
||||
deleteNoteJobs.releaseConnection();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Call Run paragraph REST API
|
||||
PostMethod postParagraph = httpPost("/notebook/job/" + noteID + "/" + paragraph.getId(), "");
|
||||
PostMethod postParagraph = httpPost("/notebook/job/" + noteId + "/" + paragraph.getId(), "");
|
||||
assertThat("test paragraph run:", postParagraph, isAllowed());
|
||||
postParagraph.releaseConnection();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Call Stop paragraph REST API
|
||||
DeleteMethod deleteParagraph = httpDelete("/notebook/job/" + noteID + "/" + paragraph.getId());
|
||||
DeleteMethod deleteParagraph = httpDelete("/notebook/job/" + noteId + "/" + paragraph.getId());
|
||||
assertThat("test paragraph stop:", deleteParagraph, isAllowed());
|
||||
deleteParagraph.releaseConnection();
|
||||
Thread.sleep(1000);
|
||||
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotebookJob() throws IOException, InterruptedException {
|
||||
LOG.info("testGetNotebookJob");
|
||||
public void testGetNoteJob() throws IOException, InterruptedException {
|
||||
LOG.info("testGetNoteJob");
|
||||
// Create note to run test.
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -414,8 +426,9 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
paragraph.setConfig(config);
|
||||
|
||||
paragraph.setText("%sh sleep 1");
|
||||
note.persist(null);
|
||||
String noteID = note.getId();
|
||||
paragraph.setAuthenticationInfo(anonymous);
|
||||
note.persist(anonymous);
|
||||
String noteId = note.getId();
|
||||
|
||||
note.runAll();
|
||||
|
||||
|
|
@ -425,12 +438,12 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
// assume that status of the paragraph is running
|
||||
GetMethod get = httpGet("/notebook/job/" + noteID);
|
||||
assertThat("test get notebook job: ", get, isAllowed());
|
||||
GetMethod get = httpGet("/notebook/job/" + noteId);
|
||||
assertThat("test get note job: ", get, isAllowed());
|
||||
String responseBody = get.getResponseBodyAsString();
|
||||
get.releaseConnection();
|
||||
|
||||
LOG.info("test get notebook job: \n" + responseBody);
|
||||
LOG.info("test get note job: \n" + responseBody);
|
||||
Map<String, Object> resp = gson.fromJson(responseBody, new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
|
||||
|
|
@ -445,19 +458,19 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
while (!paragraph.isTerminated()) {
|
||||
Thread.sleep(100);
|
||||
if (timeout++ > 10) {
|
||||
LOG.info("testGetNotebookJob timeout job.");
|
||||
LOG.info("testGetNoteJob timeout job.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunParagraphWithParams() throws IOException, InterruptedException {
|
||||
LOG.info("testRunParagraphWithParams");
|
||||
// Create note to run test.
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -467,8 +480,8 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
paragraph.setConfig(config);
|
||||
|
||||
paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)");
|
||||
note.persist(null);
|
||||
String noteID = note.getId();
|
||||
note.persist(anonymous);
|
||||
String noteId = note.getId();
|
||||
|
||||
note.runAll();
|
||||
// wait until job is finished or timeout.
|
||||
|
|
@ -482,26 +495,26 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
// Call Run paragraph REST API
|
||||
PostMethod postParagraph = httpPost("/notebook/job/" + noteID + "/" + paragraph.getId(),
|
||||
PostMethod postParagraph = httpPost("/notebook/job/" + noteId + "/" + paragraph.getId(),
|
||||
"{\"params\": {\"param\": \"hello\", \"param2\": \"world\"}}");
|
||||
assertThat("test paragraph run:", postParagraph, isAllowed());
|
||||
postParagraph.releaseConnection();
|
||||
Thread.sleep(1000);
|
||||
|
||||
Note retrNote = ZeppelinServer.notebook.getNote(noteID);
|
||||
Note retrNote = ZeppelinServer.notebook.getNote(noteId);
|
||||
Paragraph retrParagraph = retrNote.getParagraph(paragraph.getId());
|
||||
Map<String, Object> params = retrParagraph.settings.getParams();
|
||||
assertEquals("hello", params.get("param"));
|
||||
assertEquals("world", params.get("param2"));
|
||||
|
||||
//cleanup
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCronJobs() throws InterruptedException, IOException{
|
||||
public void testJobs() throws InterruptedException, IOException{
|
||||
// create a note and a paragraph
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
|
|
@ -545,21 +558,21 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
DeleteMethod deleteCron = httpDelete("/notebook/cron/" + note.getId());
|
||||
assertThat("", deleteCron, isAllowed());
|
||||
deleteCron.releaseConnection();
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegressionZEPPELIN_527() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph();
|
||||
paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)");
|
||||
|
||||
note.persist(null);
|
||||
note.persist(anonymous);
|
||||
|
||||
GetMethod getNoteJobs = httpGet("/notebook/job/" + note.getId());
|
||||
assertThat("test notebook jobs run:", getNoteJobs, isAllowed());
|
||||
assertThat("test note jobs run:", getNoteJobs, isAllowed());
|
||||
Map<String, Object> resp = gson.fromJson(getNoteJobs.getResponseBodyAsString(), new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
List<Map<String, String>> body = (List<Map<String, String>>) resp.get("body");
|
||||
|
|
@ -567,12 +580,12 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
assertFalse(body.get(0).containsKey("finished"));
|
||||
getNoteJobs.releaseConnection();
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
String jsonRequest = "{\"title\": \"title1\", \"text\": \"text1\"}";
|
||||
PostMethod post = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest);
|
||||
|
|
@ -607,17 +620,17 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
assertEquals("title2", paragraphAtIdx0.getTitle());
|
||||
assertEquals("text2", paragraphAtIdx0.getText());
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph();
|
||||
p.setTitle("hello");
|
||||
p.setText("world");
|
||||
note.persist(null);
|
||||
note.persist(anonymous);
|
||||
|
||||
GetMethod get = httpGet("/notebook/" + note.getId() + "/paragraph/" + p.getId());
|
||||
LOG.info("testGetParagraph response\n" + get.getResponseBodyAsString());
|
||||
|
|
@ -636,12 +649,12 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
assertEquals("hello", body.get("title"));
|
||||
assertEquals("world", body.get("text"));
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph();
|
||||
p.setTitle("title1");
|
||||
|
|
@ -651,7 +664,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
p2.setTitle("title2");
|
||||
p2.setText("text2");
|
||||
|
||||
note.persist(null);
|
||||
note.persist(anonymous);
|
||||
|
||||
PostMethod post = httpPost("/notebook/" + note.getId() + "/paragraph/" + p2.getId() + "/move/" + 0, "");
|
||||
assertThat("Test post method: ", post, isAllowed());
|
||||
|
|
@ -668,18 +681,18 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
assertThat("Test post method: ", post2, isBadRequest());
|
||||
post.releaseConnection();
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph();
|
||||
p.setTitle("title1");
|
||||
p.setText("text1");
|
||||
|
||||
note.persist(null);
|
||||
note.persist(anonymous);
|
||||
|
||||
DeleteMethod delete = httpDelete("/notebook/" + note.getId() + "/paragraph/" + p.getId());
|
||||
assertThat("Test delete method: ", delete, isAllowed());
|
||||
|
|
@ -689,7 +702,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Paragraph retrParagrah = retrNote.getParagraph(p.getId());
|
||||
assertNull("paragraph should be deleted", retrParagrah);
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -705,15 +718,15 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
String username = body.get("principal");
|
||||
getSecurityTicket.releaseConnection();
|
||||
|
||||
Note note1 = ZeppelinServer.notebook.createNote(null);
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
String jsonRequest = "{\"title\": \"title1\", \"text\": \"ThisIsToTestSearchMethodWithPermissions 1\"}";
|
||||
PostMethod postNotebookText = httpPost("/notebook/" + note1.getId() + "/paragraph", jsonRequest);
|
||||
postNotebookText.releaseConnection();
|
||||
PostMethod postNoteText = httpPost("/notebook/" + note1.getId() + "/paragraph", jsonRequest);
|
||||
postNoteText.releaseConnection();
|
||||
|
||||
Note note2 = ZeppelinServer.notebook.createNote(null);
|
||||
Note note2 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
jsonRequest = "{\"title\": \"title1\", \"text\": \"ThisIsToTestSearchMethodWithPermissions 2\"}";
|
||||
postNotebookText = httpPost("/notebook/" + note2.getId() + "/paragraph", jsonRequest);
|
||||
postNotebookText.releaseConnection();
|
||||
postNoteText = httpPost("/notebook/" + note2.getId() + "/paragraph", jsonRequest);
|
||||
postNoteText.releaseConnection();
|
||||
|
||||
String jsonPermissions = "{\"owners\":[\"" + username + "\"],\"readers\":[\"" + username + "\"],\"writers\":[\"" + username + "\"]}";
|
||||
PutMethod putPermission = httpPut("/notebook/" + note1.getId() + "/permissions", jsonPermissions);
|
||||
|
|
@ -723,9 +736,9 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
putPermission = httpPut("/notebook/" + note2.getId() + "/permissions", jsonPermissions);
|
||||
putPermission.releaseConnection();
|
||||
|
||||
GetMethod searchNotebook = httpGet("/notebook/search?q='ThisIsToTestSearchMethodWithPermissions'");
|
||||
searchNotebook.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> respSearchResult = gson.fromJson(searchNotebook.getResponseBodyAsString(),
|
||||
GetMethod searchNote = httpGet("/notebook/search?q='ThisIsToTestSearchMethodWithPermissions'");
|
||||
searchNote.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> respSearchResult = gson.fromJson(searchNote.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
ArrayList searchBody = (ArrayList) respSearchResult.get("body");
|
||||
|
|
@ -751,21 +764,21 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
getPermission.releaseConnection();
|
||||
}
|
||||
searchNotebook.releaseConnection();
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note2.getId(), null);
|
||||
searchNote.releaseConnection();
|
||||
ZeppelinServer.notebook.removeNote(note1.getId(), anonymous);
|
||||
ZeppelinServer.notebook.removeNote(note2.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTitleSearch() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
String jsonRequest = "{\"title\": \"testTitleSearchOfParagraph\", \"text\": \"ThisIsToTestSearchMethodWithTitle \"}";
|
||||
PostMethod postNotebookText = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest);
|
||||
postNotebookText.releaseConnection();
|
||||
PostMethod postNoteText = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest);
|
||||
postNoteText.releaseConnection();
|
||||
|
||||
GetMethod searchNotebook = httpGet("/notebook/search?q='testTitleSearchOfParagraph'");
|
||||
searchNotebook.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> respSearchResult = gson.fromJson(searchNotebook.getResponseBodyAsString(),
|
||||
GetMethod searchNote = httpGet("/notebook/search?q='testTitleSearchOfParagraph'");
|
||||
searchNote.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> respSearchResult = gson.fromJson(searchNote.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
ArrayList searchBody = (ArrayList) respSearchResult.get("body");
|
||||
|
|
@ -778,8 +791,8 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
}
|
||||
assertEquals("Paragraph title hits must be at-least one", true, numberOfTitleHits >= 1);
|
||||
searchNotebook.releaseConnection();
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
searchNote.releaseConnection();
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ import org.apache.zeppelin.notebook.Note;
|
|||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ import com.google.gson.Gson;
|
|||
*/
|
||||
public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
||||
Gson gson = new Gson();
|
||||
AuthenticationInfo anonymous;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
|
|
@ -54,6 +57,11 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
AbstractTestRestApi.shutDown();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
anonymous = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
|
||||
private void waitForFinish(Paragraph p) {
|
||||
while (p.getStatus() != Status.FINISHED
|
||||
&& p.getStatus() != Status.ERROR
|
||||
|
|
@ -69,7 +77,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
@Test
|
||||
public void basicRDDTransformationAndActionTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
// run markdown paragraph, again
|
||||
Paragraph p = note.addParagraph();
|
||||
|
|
@ -77,17 +85,18 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
p.setText("%spark print(sc.parallelize(1 to 10).reduce(_ + _))");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
assertEquals("55", p.getResult().message());
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sparkSQLTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
int sparkVersion = getSparkVersionNumber(note);
|
||||
// DataFrame API is available from spark 1.3
|
||||
if (sparkVersion >= 13) {
|
||||
|
|
@ -98,6 +107,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setConfig(config);
|
||||
p.setText("%spark val df=sqlContext.createDataFrame(Seq((\"hello\",20)))\n" +
|
||||
"df.collect()");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -111,6 +121,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setConfig(config);
|
||||
p.setText("%spark val df=sqlContext.createDataFrame(Seq((\"hello\",20)))\n" +
|
||||
"z.show(df)");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -125,20 +136,21 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setConfig(config);
|
||||
p.setText("%spark val ds=spark.createDataset(Seq((\"hello\",20)))\n" +
|
||||
"z.show(ds)");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
assertEquals(InterpreterResult.Type.TABLE, p.getResult().type());
|
||||
assertEquals("_1\t_2\nhello\t20\n", p.getResult().message());
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sparkRTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
int sparkVersion = getSparkVersionNumber(note);
|
||||
|
||||
if (isSparkR() && sparkVersion >= 14) { // sparkr supported from 1.4.0
|
||||
|
|
@ -165,19 +177,20 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
"df <- createDataFrame(" + sqlContextName + ", localDF)\n" +
|
||||
"count(df)"
|
||||
);
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
System.err.println("sparkRTest=" + p.getResult().message());
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
assertEquals("[1] 3", p.getResult().message());
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pySparkTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note.setName("note");
|
||||
int sparkVersion = getSparkVersionNumber(note);
|
||||
|
||||
|
|
@ -188,6 +201,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
p.setText("%pyspark print(sc.parallelize(range(1, 11)).reduce(lambda a, b: a + b))");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -201,6 +215,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setText("%pyspark from pyspark.sql import Row\n" +
|
||||
"df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" +
|
||||
"df.collect()");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -214,6 +229,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setText("%pyspark from pyspark.sql import Row\n" +
|
||||
"df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" +
|
||||
"z.show(df)");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -228,6 +244,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setConfig(config);
|
||||
p.setText("%pyspark sqlContext.udf.register(\"f1\", lambda x: len(x))\n" +
|
||||
"sqlContext.sql(\"select f1(\\\"abc\\\") as len\").collect()");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -242,6 +259,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
p.setText("%pyspark from pyspark.sql import Row\n" +
|
||||
"df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" +
|
||||
"df.collect()");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
@ -255,19 +273,20 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
// use SQLContext to register UDF but use this UDF through SparkSession
|
||||
p.setText("%pyspark sqlContext.udf.register(\"f1\", lambda x: len(x))\n" +
|
||||
"spark.sql(\"select f1(\\\"abc\\\") as len\").collect()");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
assertEquals("[Row(len=u'3')]\n", p.getResult().message());
|
||||
}
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pySparkAutoConvertOptionTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note.setName("note");
|
||||
|
||||
int sparkVersionNumber = getSparkVersionNumber(note);
|
||||
|
|
@ -286,33 +305,37 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
|
||||
p.setText("%pyspark\nfrom pyspark.sql.functions import *\n"
|
||||
+ "print(" + sqlContextName + ".range(0, 10).withColumn('uniform', rand(seed=10) * 3.14).count())");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
assertEquals("10\n", p.getResult().message());
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zRunTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p0 = note.addParagraph();
|
||||
Map config0 = p0.getConfig();
|
||||
config0.put("enabled", true);
|
||||
p0.setConfig(config0);
|
||||
p0.setText("%spark z.run(1)");
|
||||
p0.setAuthenticationInfo(anonymous);
|
||||
Paragraph p1 = note.addParagraph();
|
||||
Map config1 = p1.getConfig();
|
||||
config1.put("enabled", true);
|
||||
p1.setConfig(config1);
|
||||
p1.setText("%spark val a=10");
|
||||
p1.setAuthenticationInfo(anonymous);
|
||||
Paragraph p2 = note.addParagraph();
|
||||
Map config2 = p2.getConfig();
|
||||
config2.put("enabled", true);
|
||||
p2.setConfig(config2);
|
||||
p2.setText("%spark print(a)");
|
||||
p2.setAuthenticationInfo(anonymous);
|
||||
|
||||
note.run(p0.getId());
|
||||
waitForFinish(p0);
|
||||
|
|
@ -323,13 +346,13 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals(Status.FINISHED, p2.getStatus());
|
||||
assertEquals("10", p2.getResult().message());
|
||||
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pySparkDepLoaderTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(null);
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
int sparkVersionNumber = getSparkVersionNumber(note);
|
||||
|
||||
if (isPyspark() && sparkVersionNumber >= 14) {
|
||||
|
|
@ -350,6 +373,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
p0.setConfig(config);
|
||||
p0.setText("%dep z.load(\"com.databricks:spark-csv_2.11:1.2.0\")");
|
||||
p0.setAuthenticationInfo(anonymous);
|
||||
note.run(p0.getId());
|
||||
waitForFinish(p0);
|
||||
assertEquals(Status.FINISHED, p0.getStatus());
|
||||
|
|
@ -370,13 +394,14 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
"from pyspark.sql import SQLContext\n" +
|
||||
"print(" + sqlContextName + ".read.format('com.databricks.spark.csv')" +
|
||||
".load('"+ tmpFile.getAbsolutePath() +"').count())");
|
||||
p1.setAuthenticationInfo(anonymous);
|
||||
note.run(p1.getId());
|
||||
|
||||
waitForFinish(p1);
|
||||
assertEquals(Status.FINISHED, p1.getStatus());
|
||||
assertEquals("2\n", p1.getResult().message());
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -390,6 +415,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
p.setText("%spark print(sc.version)");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.zeppelin.notebook.socket.Message;
|
|||
import org.apache.zeppelin.notebook.socket.Message.OP;
|
||||
import org.apache.zeppelin.rest.AbstractTestRestApi;
|
||||
import org.apache.zeppelin.server.ZeppelinServer;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -57,6 +58,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
private static NotebookServer notebookServer;
|
||||
private static Gson gson;
|
||||
private HttpServletRequest mockRequest;
|
||||
private AuthenticationInfo anonymous;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
|
|
@ -74,6 +76,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
@Before
|
||||
public void setUp() {
|
||||
mockRequest = mock(HttpServletRequest.class);
|
||||
anonymous = new AuthenticationInfo("anonymous");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -94,14 +97,14 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
@Test
|
||||
public void testMakeSureNoAngularObjectBroadcastToWebsocketWhoFireTheEvent() throws IOException {
|
||||
// create a notebook
|
||||
Note note1 = notebook.createNote(null);
|
||||
Note note1 = notebook.createNote(anonymous);
|
||||
|
||||
// get reference to interpreterGroup
|
||||
InterpreterGroup interpreterGroup = null;
|
||||
List<InterpreterSetting> settings = notebook.getInterpreterFactory().getInterpreterSettings(note1.getId());
|
||||
for (InterpreterSetting setting : settings) {
|
||||
if (setting.getName().equals("md")) {
|
||||
interpreterGroup = setting.getInterpreterGroup("sharedProcess");
|
||||
interpreterGroup = setting.getInterpreterGroup("anonymous", "sharedProcess");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -109,6 +112,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
// start interpreter process
|
||||
Paragraph p1 = note1.addParagraph();
|
||||
p1.setText("%md start remote interpreter process");
|
||||
p1.setAuthenticationInfo(anonymous);
|
||||
note1.run(p1.getId());
|
||||
|
||||
// add angularObject
|
||||
|
|
@ -144,13 +148,13 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
verify(sock1, times(0)).send(anyString());
|
||||
verify(sock2, times(1)).send(anyString());
|
||||
|
||||
notebook.removeNote(note1.getId(), null);
|
||||
notebook.removeNote(note1.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportNotebook() throws IOException {
|
||||
String msg = "{\"op\":\"IMPORT_NOTE\",\"data\":" +
|
||||
"{\"notebook\":{\"paragraphs\": [{\"text\": \"Test " +
|
||||
"{\"note\":{\"paragraphs\": [{\"text\": \"Test " +
|
||||
"paragraphs import\",\"config\":{},\"settings\":{}}]," +
|
||||
"\"name\": \"Test Zeppelin notebook import\",\"config\": " +
|
||||
"{}}}}";
|
||||
|
|
@ -167,7 +171,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
assertNotEquals(null, notebook.getNote(note.getId()));
|
||||
assertEquals("Test Zeppelin notebook import", notebook.getNote(note.getId()).getName());
|
||||
assertEquals("Test paragraphs import", notebook.getNote(note.getId()).getParagraphs().get(0).getText());
|
||||
notebook.removeNote(note.getId(), null);
|
||||
notebook.removeNote(note.getId(), anonymous);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
"handsontable": "~0.24.2",
|
||||
"moment-duration-format": "^1.3.0",
|
||||
"select2": "^4.0.3",
|
||||
"angular-esri-map": "~2.0.0",
|
||||
"github-markdown-css": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
'xeditable',
|
||||
'ngToast',
|
||||
'focus-if',
|
||||
'ngResource',
|
||||
'esri.map'
|
||||
'ngResource'
|
||||
])
|
||||
.filter('breakFilter', function() {
|
||||
return function(text) {
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ limitations under the License.
|
|||
<th>value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="(key, value) in configurations" >
|
||||
<tr ng-repeat="key in configurations | sortByKey" >
|
||||
<td>{{key}}</td>
|
||||
<td>
|
||||
<div class="hiding_overflow">
|
||||
{{value}}
|
||||
{{configurations[key]}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
$scope.credentialInfo = [];
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
$scope.availableInterpreters = [];
|
||||
|
||||
var getCredentialInfo = function() {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/credential').
|
||||
|
|
@ -87,6 +88,33 @@
|
|||
});
|
||||
};
|
||||
|
||||
var getAvailableInterpreters = function() {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting')
|
||||
.success(function(data, status, headers, config) {
|
||||
for (var setting = 0; setting < data.body.length; setting++) {
|
||||
$scope.availableInterpreters.push(
|
||||
data.body[setting].group + '.' + data.body[setting].name);
|
||||
}
|
||||
angular.element('#entityname').autocomplete({
|
||||
source: $scope.availableInterpreters,
|
||||
select: function (event, selected){
|
||||
$scope.entity = selected.item.value;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}).error(function(data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleAddNewCredentialInfo = function() {
|
||||
if ($scope.showAddNewCredentialInfo) {
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
} else {
|
||||
$scope.showAddNewCredentialInfo = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancelCredentialInfo = function() {
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
resetCredentialInfo();
|
||||
|
|
@ -155,6 +183,7 @@
|
|||
};
|
||||
|
||||
var init = function() {
|
||||
getAvailableInterpreters();
|
||||
getCredentialInfo();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ limitations under the License.
|
|||
<i class="icon-question" ng-style="{color: showRepositoryInfo ? '#3071A9' : 'black' }"></i>
|
||||
</a>
|
||||
<button class="btn btn-default btn-sm"
|
||||
ng-click="showAddNewCredentialInfo = !showAddNewCredentialInfo">
|
||||
ng-click="toggleAddNewCredentialInfo()">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add
|
||||
</button>
|
||||
|
|
@ -59,7 +59,7 @@ limitations under the License.
|
|||
</thead>
|
||||
<tr>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="entity"></textarea>
|
||||
<input id="entityname" ng-model="entity" placeholder="[InterpreterName].[alias]"/>
|
||||
</td>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="username"></textarea>
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@
|
|||
|
||||
HomeCtrl.$inject = [
|
||||
'$scope',
|
||||
'notebookListDataFactory',
|
||||
'noteListDataFactory',
|
||||
'websocketMsgSrv',
|
||||
'$rootScope',
|
||||
'arrayOrderingSrv',
|
||||
'ngToast'
|
||||
];
|
||||
|
||||
function HomeCtrl($scope, notebookListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv, ngToast) {
|
||||
function HomeCtrl($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv, ngToast) {
|
||||
ngToast.dismiss();
|
||||
var vm = this;
|
||||
vm.notes = notebookListDataFactory;
|
||||
vm.notes = noteListDataFactory;
|
||||
vm.websocketMsgSrv = websocketMsgSrv;
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv;
|
||||
|
||||
|
|
@ -42,12 +42,12 @@
|
|||
$scope.isReloading = false;
|
||||
|
||||
var initHome = function() {
|
||||
websocketMsgSrv.getHomeNotebook();
|
||||
websocketMsgSrv.getHomeNote();
|
||||
};
|
||||
|
||||
initHome();
|
||||
|
||||
$scope.reloadNotebookList = function() {
|
||||
$scope.reloadNoteList = function() {
|
||||
websocketMsgSrv.reloadAllNotesFromRepo();
|
||||
$scope.isReloadingNotes = true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ limitations under the License.
|
|||
<h4>Notebook
|
||||
<i ng-class="isReloadingNotes ? 'fa fa-refresh fa-spin' : 'fa fa-refresh'"
|
||||
ng-style="!isReloadingNotes && {'cursor': 'pointer'}" style="font-size: 13px;"
|
||||
ng-click="reloadNotebookList();"
|
||||
ng-click="reloadNoteList();"
|
||||
tooltip-placement="bottom" tooltip="Reload notes from storage">
|
||||
</i>
|
||||
</h4>
|
||||
|
|
@ -59,15 +59,15 @@ limitations under the License.
|
|||
<i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5>
|
||||
<ul id="notebook-names">
|
||||
<li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
|
||||
<li ng-repeat="note in home.notes.list | filter:query.q | orderBy:home.arrayOrderingSrv.notebookListOrdering track by $index">
|
||||
<li ng-repeat="note in home.notes.list | filter:query.q | orderBy:home.arrayOrderingSrv.noteListOrdering track by $index">
|
||||
<i style="font-size: 10px;" class="icon-doc"></i>
|
||||
<a style="text-decoration: none;" href="#/notebook/{{note.id}}">{{noteName(note)}}</a>
|
||||
</li>
|
||||
<div ng-if="!query || query.name === ''">
|
||||
<li ng-repeat="node in home.notes.root.children | orderBy:home.arrayOrderingSrv.notebookListOrdering track by $index" ng-include="'notebook_folder_renderer.html'" />
|
||||
<li ng-repeat="node in home.notes.root.children | orderBy:home.arrayOrderingSrv.noteListOrdering track by $index" ng-include="'notebook_folder_renderer.html'" />
|
||||
</div>
|
||||
<div ng-if="query && query.name !== ''">
|
||||
<li ng-repeat="note in home.notes.flatList | filter:query.q | orderBy:home.arrayOrderingSrv.notebookListOrdering track by $index">
|
||||
<li ng-repeat="note in home.notes.flatList | filter:query.q | orderBy:home.arrayOrderingSrv.noteListOrdering track by $index">
|
||||
<i style="font-size: 10px;" class="icon-doc"></i>
|
||||
<a style="text-decoration: none;" href="#/notebook/{{note.id}}">{{noteName(note)}}</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -37,38 +37,165 @@ limitations under the License.
|
|||
|
||||
<div>
|
||||
<h5>Option</h5>
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown">
|
||||
{{getSessionOption(setting.id)}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Single interpreter instance are shared across notes"
|
||||
ng-click="setSessionOption(setting.id, 'shared')">
|
||||
shared
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setSessionOption(setting.id, 'scoped')">
|
||||
scoped
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setSessionOption(setting.id, 'isolated')">
|
||||
isolated
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<span>Interpreter for note</span>
|
||||
</div>
|
||||
<div class="row interpreter" style="margin-top: 5px;">
|
||||
<div class="col-md-6">
|
||||
The interpreter will be instantiated
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
{{getInterpreterRunningOption(setting.id)}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'shared', 'shared')">
|
||||
Globally
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'scoped', '')">
|
||||
Per Note
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="ticket.ticket !== 'anonymous' && ticket.roles !== '[]'">
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'shared', 'scoped')">
|
||||
Per User
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
in
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="getInterpreterRunningOption(setting.id) === 'Globally'">
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) !== 'Per User'">
|
||||
{{getPerNoteOption(setting.id)}}
|
||||
</span>
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) === 'Per User'">
|
||||
{{getPerUserOption(setting.id)}}
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Globally'">
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Single interpreter instance are shared across notes"
|
||||
ng-click="setPerNoteOption(setting.id, 'shared')">
|
||||
shared per note
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')">
|
||||
scoped per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerUserOption(setting.id, 'scoped')">
|
||||
scoped per user
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'isolated')">
|
||||
isolated per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerUserOption(setting.id, 'isolated')">
|
||||
isolated per user
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
process.
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) === 'Per User' && ticket.ticket !== 'anonymous' && ticket.roles !== '[]'">
|
||||
<span ng-if="getPerNoteOption(setting.id) === 'shared'">
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')"
|
||||
data-toggle="dropdown">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter"
|
||||
style="margin-top: 6px;"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'
|
||||
&& ticket.ticket !== 'anonymous'
|
||||
&& ticket.roles !== '[]'
|
||||
&& getPerNoteOption(setting.id) !== 'shared'">
|
||||
<div class="col-md-12">
|
||||
<span>
|
||||
<span class="hidden-xs" style="padding-left: 190px;">And </span>
|
||||
<span class="visible-xs" style="padding-left: 0px;">And </span>
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="true">
|
||||
<span>
|
||||
Per Note
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
</span>
|
||||
in
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown">
|
||||
<span>
|
||||
{{getPerNoteOption(setting.id)}}
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')">
|
||||
scoped per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'isolated')">
|
||||
isolated per note
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
process.
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="setPerNoteOption(setting.id, 'shared')"
|
||||
data-toggle="dropdown">
|
||||
<i class="fa fa-minus"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter" style="margin-top: 5px;">
|
||||
<div class="col-md-12">
|
||||
<div class="checkbox remove-margin-top-bottom">
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
angular.module('zeppelinWebApp').controller('InterpreterCtrl', InterpreterCtrl);
|
||||
|
||||
InterpreterCtrl.$inject = ['$scope', '$http', 'baseUrlSrv', 'ngToast', '$timeout', '$route'];
|
||||
InterpreterCtrl.$inject = ['$rootScope', '$scope', '$http', 'baseUrlSrv', 'ngToast', '$timeout', '$route'];
|
||||
|
||||
function InterpreterCtrl($scope, $http, baseUrlSrv, ngToast, $timeout, $route) {
|
||||
function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeout, $route) {
|
||||
var interpreterSettingsTmp = [];
|
||||
$scope.interpreterSettings = [];
|
||||
$scope.availableInterpreters = {};
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
interpreterSettingsTmp[index] = angular.copy($scope.interpreterSettings[index]);
|
||||
};
|
||||
|
||||
$scope.setSessionOption = function(settingId, sessionOption) {
|
||||
$scope.setPerNoteOption = function(settingId, sessionOption) {
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
|
|
@ -167,18 +167,21 @@
|
|||
}
|
||||
|
||||
if (sessionOption === 'isolated') {
|
||||
option.perNoteSession = false;
|
||||
option.perNoteProcess = true;
|
||||
option.perNote = sessionOption;
|
||||
option.session = false;
|
||||
option.process = true;
|
||||
} else if (sessionOption === 'scoped') {
|
||||
option.perNoteSession = true;
|
||||
option.perNoteProcess = false;
|
||||
option.perNote = sessionOption;
|
||||
option.session = true;
|
||||
option.process = false;
|
||||
} else {
|
||||
option.perNoteSession = false;
|
||||
option.perNoteProcess = false;
|
||||
option.perNote = 'shared';
|
||||
option.session = false;
|
||||
option.process = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getSessionOption = function(settingId) {
|
||||
$scope.setPerUserOption = function(settingId, sessionOption) {
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
|
|
@ -187,15 +190,115 @@
|
|||
var setting = $scope.interpreterSettings[index];
|
||||
option = setting.option;
|
||||
}
|
||||
if (option.perNoteSession) {
|
||||
|
||||
if (sessionOption === 'isolated') {
|
||||
option.perUser = sessionOption;
|
||||
option.session = false;
|
||||
option.process = true;
|
||||
} else if (sessionOption === 'scoped') {
|
||||
option.perUser = sessionOption;
|
||||
option.session = true;
|
||||
option.process = false;
|
||||
} else {
|
||||
option.perUser = 'shared';
|
||||
option.session = false;
|
||||
option.process = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getPerNoteOption = function(settingId) {
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
} else {
|
||||
var index = _.findIndex($scope.interpreterSettings, {'id': settingId});
|
||||
var setting = $scope.interpreterSettings[index];
|
||||
option = setting.option;
|
||||
}
|
||||
|
||||
if (option.perNote === 'scoped') {
|
||||
return 'scoped';
|
||||
} else if (option.perNoteProcess) {
|
||||
} else if (option.perNote === 'isolated') {
|
||||
return 'isolated';
|
||||
} else {
|
||||
return 'shared';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getPerUserOption = function(settingId) {
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
} else {
|
||||
var index = _.findIndex($scope.interpreterSettings, {'id': settingId});
|
||||
var setting = $scope.interpreterSettings[index];
|
||||
option = setting.option;
|
||||
}
|
||||
|
||||
if (option.perUser === 'scoped') {
|
||||
return 'scoped';
|
||||
} else if (option.perUser === 'isolated') {
|
||||
return 'isolated';
|
||||
} else {
|
||||
return 'shared';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getInterpreterRunningOption = function(settingId) {
|
||||
var sharedModeName = 'shared';
|
||||
|
||||
var globallyModeName = 'Globally';
|
||||
var perNoteModeName = 'Per Note';
|
||||
var perUserModeName = 'Per User';
|
||||
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
} else {
|
||||
var index = _.findIndex($scope.interpreterSettings, {'id': settingId});
|
||||
var setting = $scope.interpreterSettings[index];
|
||||
option = setting.option;
|
||||
}
|
||||
|
||||
var perNote = option.perNote;
|
||||
var perUser = option.perUser;
|
||||
|
||||
// Globally == shared_perNote + shared_perUser
|
||||
if (perNote === sharedModeName && perUser === sharedModeName) {
|
||||
return globallyModeName;
|
||||
}
|
||||
|
||||
if ($rootScope.ticket.ticket === 'anonymous' && $rootScope.ticket.roles === '[]') {
|
||||
if (perNote !== undefined && typeof perNote === 'string' && perNote !== '') {
|
||||
return perNoteModeName;
|
||||
}
|
||||
} else if ($rootScope.ticket.ticket !== 'anonymous') {
|
||||
if (perNote !== undefined && typeof perNote === 'string' && perNote !== '') {
|
||||
if (perUser !== undefined && typeof perUser === 'string' && perUser !== '') {
|
||||
return perUserModeName;
|
||||
}
|
||||
return perNoteModeName;
|
||||
}
|
||||
}
|
||||
|
||||
option.perNote = sharedModeName;
|
||||
option.perUser = sharedModeName;
|
||||
return globallyModeName;
|
||||
};
|
||||
|
||||
$scope.setInterpreterRunningOption = function(settingId, isPerNoteMode, isPerUserMode) {
|
||||
var option;
|
||||
if (settingId === undefined) {
|
||||
option = $scope.newInterpreterSetting.option;
|
||||
} else {
|
||||
var index = _.findIndex($scope.interpreterSettings, {'id': settingId});
|
||||
var setting = $scope.interpreterSettings[index];
|
||||
option = setting.option;
|
||||
}
|
||||
option.perNote = isPerNoteMode;
|
||||
option.perUser = isPerUserMode;
|
||||
};
|
||||
|
||||
$scope.updateInterpreterSetting = function(form, settingId) {
|
||||
var thisConfirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
|
|
@ -402,8 +505,8 @@
|
|||
remote: true,
|
||||
isExistingProcess: false,
|
||||
setPermission: false,
|
||||
perNoteSession: false,
|
||||
perNoteProcess: false
|
||||
session: false,
|
||||
process: false
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ limitations under the License.
|
|||
Interpreters
|
||||
</h3>
|
||||
<div class="pull-right" style="margin-top:10px;">
|
||||
<span style="cursor:pointer;margin-right:4px;"
|
||||
ng-click="showRepositoryInfo = !showRepositoryInfo"
|
||||
tooltip-placement="bottom" tooltip="Repository information">
|
||||
<i class="fa fa-cog" ng-style="{color: showRepositoryInfo ? '#3071A9' : 'black' }"></i>
|
||||
</span>
|
||||
<button class="btn btn-default btn-sm"
|
||||
ng-click="showRepositoryInfo = !showRepositoryInfo">
|
||||
<i class="fa fa-database" ng-style="{color: showRepositoryInfo ? '#3071A9' : 'black' }"></i>
|
||||
Repository
|
||||
</button>
|
||||
<button class="btn btn-default btn-sm"
|
||||
ng-click="showAddNewSetting = !showAddNewSetting">
|
||||
<i class="fa fa-plus"></i>
|
||||
|
|
@ -141,40 +141,172 @@ limitations under the License.
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row interpreter">
|
||||
<div class="col-md-12">
|
||||
<h5>Option</h5>
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="!valueform.$visible">
|
||||
{{getSessionOption(setting.id)}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Single interpreter instance are shared across notes"
|
||||
ng-click="setSessionOption(setting.id, 'shared')">
|
||||
shared
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setSessionOption(setting.id, 'scoped')">
|
||||
scoped
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setSessionOption(setting.id, 'isolated')">
|
||||
isolated
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<span>Interpreter for note</span>
|
||||
<div class="row interpreter" style="margin-top: 5px;">
|
||||
<div class="col-md-6">
|
||||
The interpreter will be instantiated
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="!valueform.$visible">
|
||||
{{getInterpreterRunningOption(setting.id)}} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'shared', 'shared')">
|
||||
Globally
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'scoped', '')">
|
||||
Per Note
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="ticket.ticket !== 'anonymous' && ticket.roles !== '[]'">
|
||||
<a style="cursor:pointer"
|
||||
ng-click="setInterpreterRunningOption(setting.id, 'shared', 'scoped')">
|
||||
Per User
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
in
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="!valueform.$visible
|
||||
|| getInterpreterRunningOption(setting.id) === 'Globally'">
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) !== 'Per User'">
|
||||
{{getPerNoteOption(setting.id)}}
|
||||
</span>
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) === 'Per User'">
|
||||
{{getPerUserOption(setting.id)}}
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Globally'">
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Single interpreter instance are shared across notes"
|
||||
ng-click="setPerNoteOption(setting.id, 'shared')">
|
||||
shared per note
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')">
|
||||
scoped per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerUserOption(setting.id, 'scoped')">
|
||||
scoped per user
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per Note'"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'isolated')">
|
||||
isolated per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerUserOption(setting.id, 'isolated')">
|
||||
isolated per user
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
process.
|
||||
<span ng-if="getInterpreterRunningOption(setting.id) === 'Per User' && ticket.ticket !== 'anonymous' && ticket.roles !== '[]'">
|
||||
<span ng-if="getPerNoteOption(setting.id) === 'shared'">
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')"
|
||||
ng-disabled="!valueform.$visible"
|
||||
data-toggle="dropdown">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter"
|
||||
style="margin-top: 6px;"
|
||||
ng-if="getInterpreterRunningOption(setting.id) === 'Per User'
|
||||
&& ticket.ticket !== 'anonymous'
|
||||
&& ticket.roles !== '[]'
|
||||
&& getPerNoteOption(setting.id) !== 'shared'">
|
||||
<div class="col-md-12">
|
||||
<span>
|
||||
<span class="hidden-xs" style="padding-left: 190px;">And </span>
|
||||
<span class="visible-xs" style="padding-left: 0px;">And </span>
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="true">
|
||||
<span>
|
||||
Per Note
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
</span>
|
||||
in
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
ng-disabled="!valueform.$visible">
|
||||
<span>
|
||||
{{getPerNoteOption(setting.id)}}
|
||||
</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter instance for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'scoped')">
|
||||
scoped per note
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor:pointer"
|
||||
tooltip="Separate Interpreter process for each note"
|
||||
ng-click="setPerNoteOption(setting.id, 'isolated')">
|
||||
isolated per note
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
process.
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-disabled="!valueform.$visible"
|
||||
ng-click="setPerNoteOption(setting.id, 'shared')"
|
||||
data-toggle="dropdown">
|
||||
<i class="fa fa-minus"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter" style="margin-top: 5px;">
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
$scope.jobInfomations = [];
|
||||
$scope.JobInfomationsByFilter = $scope.jobInfomations;
|
||||
|
||||
websocketMsgSrv.getNotebookJobsList();
|
||||
websocketMsgSrv.getNoteJobsList();
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
websocketMsgSrv.unsubscribeJobManager();
|
||||
|
|
@ -45,34 +45,34 @@
|
|||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('setNotebookJobs', function(event, responseData) {
|
||||
$scope.$on('setNoteJobs', function(event, responseData) {
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
|
||||
$scope.jobInfomations = responseData.jobs;
|
||||
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'notebookId') : {};
|
||||
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {};
|
||||
});
|
||||
|
||||
$scope.$on('setUpdateNotebookJobs', function(event, responseData) {
|
||||
$scope.$on('setUpdateNoteJobs', function(event, responseData) {
|
||||
var jobInfomations = $scope.jobInfomations;
|
||||
var indexStore = $scope.jobInfomationsIndexs;
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
|
||||
var notes = responseData.jobs;
|
||||
notes.map(function(changedItem) {
|
||||
if (indexStore[changedItem.notebookId] === undefined) {
|
||||
if (indexStore[changedItem.noteId] === undefined) {
|
||||
var newItem = angular.copy(changedItem);
|
||||
jobInfomations.push(newItem);
|
||||
indexStore[changedItem.notebookId] = newItem;
|
||||
indexStore[changedItem.noteId] = newItem;
|
||||
} else {
|
||||
var changeOriginTarget = indexStore[changedItem.notebookId];
|
||||
var changeOriginTarget = indexStore[changedItem.noteId];
|
||||
|
||||
if (changedItem.isRemoved !== undefined && changedItem.isRemoved === true) {
|
||||
|
||||
// remove Item.
|
||||
var removeIndex = _.findIndex(indexStore, changedItem.notebookId);
|
||||
var removeIndex = _.findIndex(indexStore, changedItem.noteId);
|
||||
if (removeIndex > -1) {
|
||||
indexStore.splice(removeIndex, 1);
|
||||
}
|
||||
|
||||
removeIndex = _.findIndex(jobInfomations, {'notebookId': changedItem.notebookId});
|
||||
removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId});
|
||||
if (removeIndex) {
|
||||
jobInfomations.splice(removeIndex, 1);
|
||||
}
|
||||
|
|
@ -80,8 +80,8 @@
|
|||
} else {
|
||||
// change value for item.
|
||||
changeOriginTarget.isRunningJob = changedItem.isRunningJob;
|
||||
changeOriginTarget.notebookName = changedItem.notebookName;
|
||||
changeOriginTarget.notebookType = changedItem.notebookType;
|
||||
changeOriginTarget.noteName = changedItem.noteName;
|
||||
changeOriginTarget.noteType = changedItem.noteType;
|
||||
changeOriginTarget.interpreter = changedItem.interpreter;
|
||||
changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun;
|
||||
changeOriginTarget.paragraphs = changedItem.paragraphs;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ limitations under the License.
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
You can monitor the written notebook. Check the status of the Notebook and can control the action.
|
||||
You can monitor the status of notebook and navigate to note or paragraph.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -41,8 +41,7 @@ limitations under the License.
|
|||
<span
|
||||
ng-switch-when="FINISHED">
|
||||
<i style="color: green; margin-right: 3px;" class="fa fa-circle"
|
||||
ng-click=""
|
||||
tooltip="FINISHED">
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
@ -50,8 +49,7 @@ limitations under the License.
|
|||
ng-switch-when="RUNNING"
|
||||
style="margin-right: 3px;">
|
||||
<i style="color: blue" class="fa fa-spinner"
|
||||
ng-click=""
|
||||
tooltip="RUNNING">
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
@ -59,8 +57,7 @@ limitations under the License.
|
|||
ng-switch-when="READY"
|
||||
style="margin-right: 3px;">
|
||||
<i style="color: green" class="fa fa-circle-o"
|
||||
ng-click=""
|
||||
tooltip="READY">
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
@ -68,9 +65,7 @@ limitations under the License.
|
|||
ng-switch-when="PENDING"
|
||||
style="margin-right: 3px;">
|
||||
<i style="color: gray" class="fa fa-circle"
|
||||
ng-click=""
|
||||
tooltip="PENDING"
|
||||
>
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
@ -78,8 +73,7 @@ limitations under the License.
|
|||
ng-switch-when="ABORT"
|
||||
style="margin-right: 3px;">
|
||||
<i style="color: orange" class="fa fa-circle"
|
||||
ng-click=""
|
||||
tooltip="ABORT">
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
@ -87,8 +81,7 @@ limitations under the License.
|
|||
ng-switch-when="ERROR"
|
||||
style="margin-right: 3px;">
|
||||
<i style="color: red" class="fa fa-circle"
|
||||
ng-click=""
|
||||
tooltip="ERROR">
|
||||
ng-click="">
|
||||
</i>
|
||||
{{jobStatus}}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
-->
|
||||
|
||||
<div id="{{notebookJob.notebookId}}_control" class="control">
|
||||
<div id="{{notebookJob.noteId}}_control" class="control">
|
||||
<span>
|
||||
{{lastExecuteTime(notebookJob.unixTimeLastRun)}}
|
||||
</span>
|
||||
<span>
|
||||
<span ng-if="notebookJob.isRunningJob === true">
|
||||
Notebook is RUNNING
|
||||
Note is RUNNING
|
||||
</span>
|
||||
<span ng-if="notebookJob.isRunningJob === false">
|
||||
Notebook is READY
|
||||
Note is READY
|
||||
</span>
|
||||
</span>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
-->
|
||||
|
||||
<div id="{{notebookJob.notebookId}}_runControl" class="runControl">
|
||||
<div id="{{notebookJob.notebookId}}_progress" class="progress" ng-if="notebookJob.isRunningJob === true">
|
||||
<div id="{{notebookJob.noteId}}_runControl" class="runControl">
|
||||
<div id="{{notebookJob.noteId}}_progress" class="progress" ng-if="notebookJob.isRunningJob === true">
|
||||
<div ng-if="getProgress()>0 && getProgress()<100 && notebookJob.isRunningJob === true"
|
||||
class="progress-bar" role="progressbar" ng-style="{width:getProgress()+'%'}"></div>
|
||||
<div ng-if="(getProgress()<=0 || getProgress()>=100) && (notebookJob.isRunningJob === true)"
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ limitations under the License.
|
|||
<div ng-include src="'app/jobmanager/jobs/job-control.html'"></div>
|
||||
<span
|
||||
class="job-types"
|
||||
ng-switch="notebookJob.notebookType">
|
||||
ng-switch="notebookJob.noteType">
|
||||
<i ng-switch-when="normal" class="icon-doc"></i>
|
||||
<i ng-switch-when="cron" class="icon-clock"></i>
|
||||
<i ng-switch-default class="icon-question"></i>
|
||||
</span>
|
||||
|
||||
<a style="text-decoration: none !important;" ng-href="#/notebook/{{notebookJob.notebookId}}">
|
||||
<a style="text-decoration: none !important;" ng-href="#/notebook/{{notebookJob.noteId}}">
|
||||
<span>
|
||||
{{notebookJob.notebookName}}
|
||||
{{notebookJob.noteName}}
|
||||
</span>
|
||||
<span>
|
||||
-
|
||||
|
|
@ -48,7 +48,7 @@ limitations under the License.
|
|||
ng-switch="paragraphJob.status">
|
||||
<a ng-switch-when="READY"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: green" class="fa fa-circle-o"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is READY">
|
||||
|
|
@ -56,7 +56,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-when="FINISHED"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: green" class="fa fa-circle"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is FINISHED">
|
||||
|
|
@ -64,7 +64,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-when="ABORT"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: orange" class="fa fa-circle"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is ABORT">
|
||||
|
|
@ -72,7 +72,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-when="ERROR"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: red" class="fa fa-circle"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is ERROR">
|
||||
|
|
@ -80,7 +80,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-when="PENDING"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: gray" class="fa fa-circle"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is PENDING">
|
||||
|
|
@ -88,7 +88,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-when="RUNNING"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i style="color: blue" class="fa fa-spinner spinAnimation"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is RUNNING">
|
||||
|
|
@ -96,7 +96,7 @@ limitations under the License.
|
|||
</a>
|
||||
<a ng-switch-default class="icon-question"
|
||||
style="text-decoration: none !important;"
|
||||
ng-href="#/notebook/{{notebookJob.notebookId}}/paragraph/{{paragraphJob.id}}">
|
||||
ng-href="#/notebook/{{notebookJob.noteId}}/paragraph/{{paragraphJob.id}}">
|
||||
<i class="icon-question"
|
||||
tooltip-placement="top-left"
|
||||
tooltip="{{paragraphJob.name}} is {{paragraphJob.status}}">
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ limitations under the License.
|
|||
<button type="button"
|
||||
class="btn btn-default btn-xs"
|
||||
ng-hide="viewOnly"
|
||||
tooltip-placement="bottom" tooltip="Clone the notebook" data-source-note-name="{{note.name}}"
|
||||
tooltip-placement="bottom" tooltip="Clone this note" data-source-note-name="{{note.name}}"
|
||||
data-toggle="modal" data-target="#noteNameModal" data-clone="true"
|
||||
>
|
||||
<i class="fa fa-copy"></i>
|
||||
|
|
@ -61,8 +61,8 @@ limitations under the License.
|
|||
<button type="button"
|
||||
class="btn btn-default btn-xs"
|
||||
ng-hide="viewOnly"
|
||||
ng-click="exportNotebook()"
|
||||
tooltip-placement="bottom" tooltip="Export the notebook">
|
||||
ng-click="exportNote()"
|
||||
tooltip-placement="bottom" tooltip="Export this note">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
</span>
|
||||
|
|
@ -91,9 +91,9 @@ limitations under the License.
|
|||
<button type="button"
|
||||
class="btn btn-default btn-xs"
|
||||
ng-hide="viewOnly"
|
||||
ng-click="checkpointNotebook(note.checkpoint.message)"
|
||||
ng-click="checkpointNote(note.checkpoint.message)"
|
||||
style="margin-left: 4px;"
|
||||
tooltip-placement="bottom" tooltip="Commit the notebook">Commit
|
||||
tooltip-placement="bottom" tooltip="Commit this note">Commit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -130,7 +130,7 @@ limitations under the License.
|
|||
class="btn btn-default btn-xs"
|
||||
ng-click="removeNote(note.id)"
|
||||
ng-hide="viewOnly"
|
||||
tooltip-placement="bottom" tooltip="Remove the notebook">
|
||||
tooltip-placement="bottom" tooltip="Remove this note">
|
||||
<i class="icon-trash"></i>
|
||||
</button>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
|
||||
/** Init the new controller */
|
||||
var initNotebook = function() {
|
||||
websocketMsgSrv.getNotebook($routeParams.noteId);
|
||||
websocketMsgSrv.getNote($routeParams.noteId);
|
||||
websocketMsgSrv.listRevisionHistory($routeParams.noteId);
|
||||
var currentRoute = $route.current;
|
||||
if (currentRoute) {
|
||||
|
|
@ -145,10 +145,10 @@
|
|||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Do you want to delete this notebook?',
|
||||
message: 'Do you want to delete this note?',
|
||||
callback: function(result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.deleteNotebook(noteId);
|
||||
websocketMsgSrv.deleteNote(noteId);
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
};
|
||||
|
||||
//Export notebook
|
||||
$scope.exportNotebook = function() {
|
||||
$scope.exportNote = function() {
|
||||
var jsonContent = JSON.stringify($scope.note);
|
||||
saveAsService.saveAs(jsonContent, $scope.note.name, 'json');
|
||||
};
|
||||
|
|
@ -166,10 +166,10 @@
|
|||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Do you want to clone this notebook?',
|
||||
message: 'Do you want to clone this note?',
|
||||
callback: function(result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.cloneNotebook(noteId);
|
||||
websocketMsgSrv.cloneNote(noteId);
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
|
|
@ -177,14 +177,14 @@
|
|||
};
|
||||
|
||||
// checkpoint/commit notebook
|
||||
$scope.checkpointNotebook = function(commitMessage) {
|
||||
$scope.checkpointNote = function(commitMessage) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Commit notebook to current repository?',
|
||||
message: 'Commit note to current repository?',
|
||||
callback: function(result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.checkpointNotebook($routeParams.noteId, commitMessage);
|
||||
websocketMsgSrv.checkpointNote($routeParams.noteId, commitMessage);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -336,13 +336,13 @@
|
|||
if (config) {
|
||||
$scope.note.config = config;
|
||||
}
|
||||
websocketMsgSrv.updateNotebook($scope.note.id, $scope.note.name, $scope.note.config);
|
||||
websocketMsgSrv.updateNote($scope.note.id, $scope.note.name, $scope.note.config);
|
||||
};
|
||||
|
||||
/** Update the note name */
|
||||
$scope.sendNewName = function() {
|
||||
if ($scope.note.name) {
|
||||
websocketMsgSrv.updateNotebook($scope.note.id, $scope.note.name, $scope.note.config);
|
||||
websocketMsgSrv.updateNote($scope.note.id, $scope.note.name, $scope.note.config);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
|||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-if="paragraph.result.type == 'TABLE'"
|
||||
ng-class="{'active': isGraphMode('table')}"
|
||||
ng-click="setGraphMode('table', true)"
|
||||
ng-click="setGraphMode('table', true)"
|
||||
tooltip="Table" tooltip-placement="bottom"><i class="fa fa-table"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
|
|
@ -53,12 +53,6 @@ limitations under the License.
|
|||
ng-click="setGraphMode('scatterChart', true)"
|
||||
tooltip="Scatter Chart" tooltip-placement="bottom"><i class="cf cf-scatter-chart"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-if="paragraph.result.type == 'TABLE'"
|
||||
ng-class="{'active': isGraphMode('map')}"
|
||||
ng-click="setGraphMode('map', true)"
|
||||
tooltip="Map" tooltip-placement="bottom"><i class="fa fa-map-marker"></i>
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
ng-if="paragraph.result.type != 'TABLE'"
|
||||
|
|
|
|||
|
|
@ -51,12 +51,4 @@ limitations under the License.
|
|||
id="p{{paragraph.id}}_scatterChart">
|
||||
<svg></svg>
|
||||
</div>
|
||||
|
||||
<div ng-if="getGraphMode()=='map'" id="p{{paragraph.id}}_map"
|
||||
ng-switch="paragraph.config.graph.map.isOnline">
|
||||
<div ng-switch-when="true"></div>
|
||||
<span class="map-offline-text" ng-switch-default>
|
||||
<span>Maps require internet connectivity.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ limitations under the License.
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="getGraphMode()!='scatterChart' && getGraphMode()!='map'">
|
||||
<div class="row" ng-if="getGraphMode()!='scatterChart'">
|
||||
<div class="col-md-4">
|
||||
<span class="columns lightBold">
|
||||
Keys
|
||||
|
|
@ -165,52 +165,4 @@ limitations under the License.
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="getGraphMode()=='map'">
|
||||
<div class="col-md-4">
|
||||
<span class="columns lightBold">
|
||||
Latitude
|
||||
<ul data-drop="true"
|
||||
ng-model="paragraph.config.graph.map.lat"
|
||||
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
|
||||
class="list-unstyled">
|
||||
<li ng-if="paragraph.config.graph.map.lat">
|
||||
<div class="btn btn-primary btn-xs">
|
||||
{{paragraph.config.graph.map.lat.name}} <span class="fa fa-close" ng-click="removeMapOptionLat($index)"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="columns lightBold">
|
||||
Longitude
|
||||
<ul data-drop="true"
|
||||
ng-model="paragraph.config.graph.map.lng"
|
||||
jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
|
||||
class="list-unstyled">
|
||||
<li ng-if="paragraph.config.graph.map.lng">
|
||||
<div class="btn btn-primary btn-xs">
|
||||
{{paragraph.config.graph.map.lng.name}} <span class="fa fa-close" ng-click="removeMapOptionLng($index)"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="columns lightBold">
|
||||
Pin contents
|
||||
<ul data-drop="true"
|
||||
ng-model="paragraph.config.graph.map.pinCols"
|
||||
jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
|
||||
class="list-unstyled">
|
||||
<li ng-repeat="col in paragraph.config.graph.map.pinCols">
|
||||
<div class="btn btn-primary btn-xs">
|
||||
{{col.name}} <span class="fa fa-close" ng-click="removeMapOptionPinInfo($index)"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@
|
|||
'websocketMsgSrv',
|
||||
'baseUrlSrv',
|
||||
'ngToast',
|
||||
'saveAsService',
|
||||
'esriLoader'
|
||||
'saveAsService'
|
||||
];
|
||||
|
||||
function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $location,
|
||||
$timeout, $compile, $http, $q, websocketMsgSrv,
|
||||
baseUrlSrv, ngToast, saveAsService, esriLoader) {
|
||||
baseUrlSrv, ngToast, saveAsService) {
|
||||
var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
|
||||
$scope.parentNote = null;
|
||||
$scope.paragraph = null;
|
||||
|
|
@ -105,7 +104,6 @@
|
|||
$scope.parentNote = note;
|
||||
$scope.originalText = angular.copy(newParagraph.text);
|
||||
$scope.chart = {};
|
||||
$scope.baseMapOption = ['Streets', 'Satellite', 'Hybrid', 'Topo', 'Gray', 'Oceans', 'Terrain'];
|
||||
$scope.colWidthOption = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
||||
$scope.paragraphFocused = false;
|
||||
if (newParagraph.focus) {
|
||||
|
|
@ -257,22 +255,6 @@
|
|||
config.graph.scatter = {};
|
||||
}
|
||||
|
||||
if (!config.graph.map) {
|
||||
config.graph.map = {};
|
||||
}
|
||||
|
||||
if (!config.graph.map.baseMapType) {
|
||||
config.graph.map.baseMapType = $scope.baseMapOption[0];
|
||||
}
|
||||
|
||||
if (!config.graph.map.isOnline) {
|
||||
config.graph.map.isOnline = true;
|
||||
}
|
||||
|
||||
if (!config.graph.map.pinCols) {
|
||||
config.graph.map.pinCols = [];
|
||||
}
|
||||
|
||||
if (config.enabled === undefined) {
|
||||
config.enabled = true;
|
||||
}
|
||||
|
|
@ -974,8 +956,6 @@
|
|||
|
||||
if (!type || type === 'table') {
|
||||
setTable($scope.paragraph.result, refresh);
|
||||
} else if (type === 'map') {
|
||||
setMap($scope.paragraph.result, refresh);
|
||||
} else {
|
||||
setD3Chart(type, $scope.paragraph.result, refresh);
|
||||
}
|
||||
|
|
@ -1206,236 +1186,6 @@
|
|||
$timeout(retryRenderer);
|
||||
};
|
||||
|
||||
var setMap = function(data, refresh) {
|
||||
var createPinMapLayer = function(pins, cb) {
|
||||
esriLoader.require(['esri/layers/FeatureLayer'], function(FeatureLayer) {
|
||||
var pinLayer = new FeatureLayer({
|
||||
id: 'pins',
|
||||
spatialReference: $scope.map.spatialReference,
|
||||
geometryType: 'point',
|
||||
source: pins,
|
||||
fields: [],
|
||||
objectIdField: '_ObjectID',
|
||||
renderer: $scope.map.pinRenderer,
|
||||
popupTemplate: {
|
||||
title: '[{_lng}, {_lat}]',
|
||||
content: [{
|
||||
type: 'fields',
|
||||
fieldInfos: []
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
// add user-selected pin info fields to popup
|
||||
var pinInfoCols = $scope.paragraph.config.graph.map.pinCols;
|
||||
for (var i = 0; i < pinInfoCols.length; ++i) {
|
||||
pinLayer.popupTemplate.content[0].fieldInfos.push({
|
||||
fieldName: pinInfoCols[i].name,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
cb(pinLayer);
|
||||
});
|
||||
};
|
||||
|
||||
var getMapPins = function(cb) {
|
||||
esriLoader.require(['esri/geometry/Point'], function(Point, FeatureLayer) {
|
||||
var latCol = $scope.paragraph.config.graph.map.lat;
|
||||
var lngCol = $scope.paragraph.config.graph.map.lng;
|
||||
var pinInfoCols = $scope.paragraph.config.graph.map.pinCols;
|
||||
var pins = [];
|
||||
|
||||
// construct objects for pins
|
||||
if (latCol && lngCol && data.rows) {
|
||||
for (var i = 0; i < data.rows.length; ++i) {
|
||||
var row = data.rows[i];
|
||||
var lng = row[lngCol.index];
|
||||
var lat = row[latCol.index];
|
||||
var pin = {
|
||||
geometry: new Point({
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
spatialReference: $scope.map.spatialReference
|
||||
}),
|
||||
attributes: {
|
||||
_ObjectID: i,
|
||||
_lng: lng,
|
||||
_lat: lat
|
||||
}
|
||||
};
|
||||
|
||||
// add pin info from user-selected columns
|
||||
for (var j = 0; j < pinInfoCols.length; ++j) {
|
||||
var col = pinInfoCols[j];
|
||||
pin.attributes[col.name] = row[col.index];
|
||||
}
|
||||
pins.push(pin);
|
||||
}
|
||||
}
|
||||
cb(pins);
|
||||
});
|
||||
};
|
||||
|
||||
var updateMapPins = function() {
|
||||
var pinLayer = $scope.map.map.findLayerById('pins');
|
||||
$scope.map.popup.close();
|
||||
if (pinLayer) {
|
||||
$scope.map.map.remove(pinLayer);
|
||||
}
|
||||
|
||||
// add pins to map as layer
|
||||
getMapPins(function(pins) {
|
||||
createPinMapLayer(pins, function(pinLayer) {
|
||||
$scope.map.map.add(pinLayer);
|
||||
if (pinLayer.source.length > 0) {
|
||||
$scope.map.goTo(pinLayer.source);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var createMap = function(mapdiv) {
|
||||
// prevent zooming with the scroll wheel
|
||||
var disableZoom = function(e) {
|
||||
var evt = e || window.event;
|
||||
evt.cancelBubble = true;
|
||||
evt.returnValue = false;
|
||||
if (evt.stopPropagation) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
};
|
||||
var eName = window.WheelEvent ? 'wheel' : // Modern browsers
|
||||
window.MouseWheelEvent ? 'mousewheel' : // WebKit and IE
|
||||
'DOMMouseScroll'; // Old Firefox
|
||||
mapdiv.addEventListener(eName, disableZoom, true);
|
||||
|
||||
esriLoader.require(['esri/views/MapView',
|
||||
'esri/Map',
|
||||
'esri/renderers/SimpleRenderer',
|
||||
'esri/symbols/SimpleMarkerSymbol'],
|
||||
function(MapView, Map, SimpleRenderer, SimpleMarkerSymbol) {
|
||||
$scope.map = new MapView({
|
||||
container: mapdiv,
|
||||
map: new Map({
|
||||
basemap: $scope.paragraph.config.graph.map.baseMapType.toLowerCase()
|
||||
}),
|
||||
center: [-106.3468, 56.1304], // Canada (lng, lat)
|
||||
zoom: 2,
|
||||
pinRenderer: new SimpleRenderer({
|
||||
symbol: new SimpleMarkerSymbol({
|
||||
'color': [255, 0, 0, 0.5],
|
||||
'size': 16.5,
|
||||
'outline': {
|
||||
'color': [0, 0, 0, 1],
|
||||
'width': 1.125,
|
||||
},
|
||||
// map pin SVG path
|
||||
'path': 'M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,' +
|
||||
'18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z ' +
|
||||
'M16,14.584c-1.979,0-3.584-1.604-3.584-3.584S14.021,7.416,' +
|
||||
'16,7.416S19.584,9.021,19.584,11S17.979,14.584,16,14.584z'
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
$scope.map.on('click', function() {
|
||||
// ArcGIS JS API 4.0 does not account for scrolling or position
|
||||
// changes by default (this is a bug, to be fixed in the upcoming
|
||||
// version 4.1; see https://geonet.esri.com/thread/177238#comment-609681).
|
||||
// This results in a misaligned popup.
|
||||
|
||||
// Workaround: manually set popup position to match position of selected pin
|
||||
if ($scope.map.popup.selectedFeature) {
|
||||
$scope.map.popup.location = $scope.map.popup.selectedFeature.geometry;
|
||||
}
|
||||
});
|
||||
$scope.map.then(updateMapPins);
|
||||
});
|
||||
};
|
||||
|
||||
var checkMapOnline = function(cb) {
|
||||
// are we able to get a response from the ArcGIS servers?
|
||||
var callback = function(res) {
|
||||
var online = (res.status > 0);
|
||||
$scope.paragraph.config.graph.map.isOnline = online;
|
||||
cb(online);
|
||||
};
|
||||
$http.head('//services.arcgisonline.com/arcgis/', {
|
||||
timeout: 5000,
|
||||
withCredentials: false
|
||||
}).then(callback, callback);
|
||||
};
|
||||
|
||||
var renderMap = function() {
|
||||
var mapdiv = angular.element('#p' + $scope.paragraph.id + '_map')
|
||||
.css('height', $scope.paragraph.config.graph.height)
|
||||
.children('div').get(0);
|
||||
|
||||
// on chart type change, destroy map to force reinitialization.
|
||||
if ($scope.map && !refresh) {
|
||||
$scope.map.map.destroy();
|
||||
$scope.map.pinRenderer = null;
|
||||
$scope.map = null;
|
||||
}
|
||||
|
||||
var requireMapCSS = function() {
|
||||
var url = '//js.arcgis.com/4.0/esri/css/main.css';
|
||||
if (!angular.element('link[href="' + url + '"]').length) {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = url;
|
||||
angular.element('head').append(link);
|
||||
}
|
||||
};
|
||||
|
||||
var requireMapJS = function(cb) {
|
||||
if (!esriLoader.isLoaded()) {
|
||||
esriLoader.bootstrap({
|
||||
url: '//js.arcgis.com/4.0'
|
||||
}).then(cb);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
checkMapOnline(function(online) {
|
||||
// we need an internet connection to use the map
|
||||
if (online) {
|
||||
// create map if not exists.
|
||||
if (!$scope.map) {
|
||||
requireMapCSS();
|
||||
requireMapJS(function() {
|
||||
createMap(mapdiv);
|
||||
});
|
||||
} else {
|
||||
updateMapPins();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var retryRenderer = function() {
|
||||
if (angular.element('#p' + $scope.paragraph.id + '_map div').length) {
|
||||
try {
|
||||
renderMap();
|
||||
} catch (err) {
|
||||
console.log('Map drawing error %o', err);
|
||||
}
|
||||
} else {
|
||||
$timeout(retryRenderer,10);
|
||||
}
|
||||
};
|
||||
$timeout(retryRenderer);
|
||||
};
|
||||
|
||||
$scope.setMapBaseMap = function(bm) {
|
||||
$scope.paragraph.config.graph.map.baseMapType = bm;
|
||||
if ($scope.map) {
|
||||
$scope.map.map.basemap = bm.toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isGraphMode = function(graphName) {
|
||||
var activeAppId = _.get($scope.paragraph.config, 'helium.activeApp');
|
||||
if ($scope.getResultType() === 'TABLE' && $scope.getGraphMode() === graphName && !activeAppId) {
|
||||
|
|
@ -1498,24 +1248,6 @@
|
|||
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
|
||||
};
|
||||
|
||||
$scope.removeMapOptionLat = function(idx) {
|
||||
$scope.paragraph.config.graph.map.lat = null;
|
||||
clearUnknownColsFromGraphOption();
|
||||
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
|
||||
};
|
||||
|
||||
$scope.removeMapOptionLng = function(idx) {
|
||||
$scope.paragraph.config.graph.map.lng = null;
|
||||
clearUnknownColsFromGraphOption();
|
||||
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
|
||||
};
|
||||
|
||||
$scope.removeMapOptionPinInfo = function(idx) {
|
||||
$scope.paragraph.config.graph.map.pinCols.splice(idx, 1);
|
||||
clearUnknownColsFromGraphOption();
|
||||
$scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
|
||||
};
|
||||
|
||||
/* Clear unknown columns from graph option */
|
||||
var clearUnknownColsFromGraphOption = function() {
|
||||
var unique = function(list) {
|
||||
|
|
@ -1574,10 +1306,6 @@
|
|||
removeUnknown($scope.paragraph.config.graph.groups);
|
||||
|
||||
removeUnknownFromFields($scope.paragraph.config.graph.scatter);
|
||||
|
||||
unique($scope.paragraph.config.graph.map.pinCols);
|
||||
removeUnknown($scope.paragraph.config.graph.map.pinCols);
|
||||
removeUnknownFromFields($scope.paragraph.config.graph.map);
|
||||
};
|
||||
|
||||
/* select default key and value if there're none selected */
|
||||
|
|
@ -1598,23 +1326,6 @@
|
|||
$scope.paragraph.config.graph.scatter.xAxis = $scope.paragraph.result.columnNames[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* try to find columns for the map logitude and latitude */
|
||||
var findDefaultMapCol = function(settingName, keyword) {
|
||||
var col;
|
||||
if (!$scope.paragraph.config.graph.map[settingName]) {
|
||||
for (var i = 0; i < $scope.paragraph.result.columnNames.length; ++i) {
|
||||
col = $scope.paragraph.result.columnNames[i];
|
||||
if (col.name.toUpperCase().indexOf(keyword) !== -1) {
|
||||
$scope.paragraph.config.graph.map[settingName] = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
findDefaultMapCol('lat', 'LAT');
|
||||
findDefaultMapCol('lng', 'LONG');
|
||||
};
|
||||
|
||||
var pivot = function(data) {
|
||||
|
|
|
|||
|
|
@ -331,41 +331,12 @@ table.dataTable.table-condensed .sorting_desc:after {
|
|||
.tableDisplay div {
|
||||
}
|
||||
|
||||
.tableDisplay img:not(.esri-bitmap) {
|
||||
.tableDisplay img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.esri-display-object > svg {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.esri-popup > .esri-docked.esri-dock-to-bottom {
|
||||
padding: 8px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.esri-popup-main {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
span.map-offline-text {
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.map-offline-text > span {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
.tableDisplay .btn-group span {
|
||||
margin: 10px 0 0 10px;
|
||||
font-size: 12px;
|
||||
|
|
@ -383,8 +354,7 @@ span.map-offline-text > span {
|
|||
|
||||
}
|
||||
|
||||
.tableDisplay .option .columns,
|
||||
div.esri-view {
|
||||
.tableDisplay .option .columns {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
function arrayOrderingSrv() {
|
||||
var arrayOrderingSrv = this;
|
||||
|
||||
this.notebookListOrdering = function(note) {
|
||||
this.noteListOrdering = function(note) {
|
||||
return arrayOrderingSrv.getNoteName(note);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ limitations under the License.
|
|||
</div>
|
||||
<div class="expandable" style="color: black;">
|
||||
<ul>
|
||||
<li ng-repeat="note in note.children | orderBy:navbar.arrayOrderingSrv.notebookListOrdering track by $index"
|
||||
<li ng-repeat="note in note.children | orderBy:navbar.arrayOrderingSrv.noteListOrdering track by $index"
|
||||
ng-class="{'active' : navbar.isActive(note.id)}"
|
||||
ng-include="'components/navbar/navbar-notebookList-elem.html'">
|
||||
ng-include="'components/navbar/navbar-noteList-elem.html'">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
'$http',
|
||||
'$routeParams',
|
||||
'$location',
|
||||
'notebookListDataFactory',
|
||||
'noteListDataFactory',
|
||||
'baseUrlSrv',
|
||||
'websocketMsgSrv',
|
||||
'arrayOrderingSrv',
|
||||
|
|
@ -30,14 +30,14 @@
|
|||
];
|
||||
|
||||
function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
|
||||
notebookListDataFactory, baseUrlSrv, websocketMsgSrv,
|
||||
noteListDataFactory, baseUrlSrv, websocketMsgSrv,
|
||||
arrayOrderingSrv, searchService) {
|
||||
var vm = this;
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv;
|
||||
vm.connected = websocketMsgSrv.isConnected();
|
||||
vm.isActive = isActive;
|
||||
vm.logout = logout;
|
||||
vm.notes = notebookListDataFactory;
|
||||
vm.notes = noteListDataFactory;
|
||||
vm.search = search;
|
||||
vm.searchForm = searchService;
|
||||
vm.showLoginWindow = showLoginWindow;
|
||||
|
|
@ -57,6 +57,7 @@
|
|||
}
|
||||
|
||||
function initController() {
|
||||
$scope.isDrawNavbarNoteList = false;
|
||||
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});
|
||||
|
||||
angular.element(document).click(function() {
|
||||
|
|
@ -72,7 +73,7 @@
|
|||
}
|
||||
|
||||
function loadNotes() {
|
||||
websocketMsgSrv.getNotebookList();
|
||||
websocketMsgSrv.getNoteList();
|
||||
}
|
||||
|
||||
function logout() {
|
||||
|
|
@ -112,7 +113,7 @@
|
|||
*/
|
||||
|
||||
$scope.$on('setNoteMenu', function(event, notes) {
|
||||
notebookListDataFactory.setNotes(notes);
|
||||
noteListDataFactory.setNotes(notes);
|
||||
});
|
||||
|
||||
$scope.$on('setConnectedStatus', function(event, param) {
|
||||
|
|
@ -122,6 +123,19 @@
|
|||
$scope.$on('loginSuccess', function(event, param) {
|
||||
loadNotes();
|
||||
});
|
||||
|
||||
/*
|
||||
** Performance optimization for Browser Render.
|
||||
*/
|
||||
angular.element(document).ready(function() {
|
||||
angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
|
||||
$scope.isDrawNavbarNoteList = true;
|
||||
});
|
||||
|
||||
angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
|
||||
$scope.isDrawNavbarNoteList = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -25,15 +25,15 @@ limitations under the License.
|
|||
|
||||
<div class="collapse navbar-collapse" ng-controller="NavCtrl as navbar">
|
||||
<ul class="nav navbar-nav" ng-if="ticket">
|
||||
<li class="dropdown" dropdown>
|
||||
<a href="#" class="dropdown-toggle" dropdown-toggle>Notebook <span class="caret"></span></a>
|
||||
<li class="dropdown notebook-list-dropdown" dropdown>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" dropdown-toggle>Notebook <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-dropdown-maxHeight" role="menu">
|
||||
<li><a href="" data-toggle="modal" data-target="#noteNameModal"><i class="fa fa-plus"></i> Create new note</a></li>
|
||||
<li class="divider"></li>
|
||||
<div id="notebook-list" class="scrollbar-container">
|
||||
<div id="notebook-list" class="scrollbar-container" ng-if="isDrawNavbarNoteList">
|
||||
<li class="filter-names" ng-include="'components/filterNoteNames/filter-note-names.html'"></li>
|
||||
<li ng-repeat="note in navbar.notes.root.children | filter:query.q | orderBy:navbar.arrayOrderingSrv.notebookListOrdering track by $index"
|
||||
ng-class="{'active' : navbar.isActive(note.id)}" ng-include="'components/navbar/navbar-notebookList-elem.html'">
|
||||
<li ng-repeat="note in navbar.notes.root.children | filter:query.q | orderBy:navbar.arrayOrderingSrv.noteListOrdering track by $index"
|
||||
ng-class="{'active' : navbar.isActive(note.id)}" ng-include="'components/navbar/navbar-noteList-elem.html'">
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
|
|
@ -57,7 +57,7 @@ limitations under the License.
|
|||
id="searchTermId"
|
||||
ng-disabled="!navbar.connected"
|
||||
class="form-control"
|
||||
placeholder="Search your Notebooks"
|
||||
placeholder="Search your Notes"
|
||||
/>
|
||||
<span class="input-group-btn">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
'use strict';
|
||||
(function() {
|
||||
|
||||
angular.module('zeppelinWebApp').factory('notebookListDataFactory', notebookListDataFactory);
|
||||
angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory);
|
||||
|
||||
function notebookListDataFactory() {
|
||||
function noteListDataFactory() {
|
||||
var notes = {
|
||||
root: {children: []},
|
||||
flatList: [],
|
||||
|
|
@ -18,24 +18,24 @@
|
|||
|
||||
NotenameCtrl.$inject = [
|
||||
'$scope',
|
||||
'notebookListDataFactory',
|
||||
'noteListDataFactory',
|
||||
'$routeParams',
|
||||
'websocketMsgSrv'
|
||||
];
|
||||
|
||||
function NotenameCtrl($scope, notebookListDataFactory, $routeParams, websocketMsgSrv) {
|
||||
function NotenameCtrl($scope, noteListDataFactory, $routeParams, websocketMsgSrv) {
|
||||
var vm = this;
|
||||
vm.clone = false;
|
||||
vm.notes = notebookListDataFactory;
|
||||
vm.notes = noteListDataFactory;
|
||||
vm.websocketMsgSrv = websocketMsgSrv;
|
||||
$scope.note = {};
|
||||
|
||||
vm.createNote = function() {
|
||||
if (!vm.clone) {
|
||||
vm.websocketMsgSrv.createNotebook($scope.note.notename);
|
||||
vm.websocketMsgSrv.createNote($scope.note.notename);
|
||||
} else {
|
||||
var noteId = $routeParams.noteId;
|
||||
vm.websocketMsgSrv.cloneNotebook(noteId, $scope.note.notename);
|
||||
vm.websocketMsgSrv.cloneNote(noteId, $scope.note.notename);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
} else {
|
||||
result.name = $scope.note.noteImportName;
|
||||
}
|
||||
websocketMsgSrv.importNotebook(result);
|
||||
websocketMsgSrv.importNote(result);
|
||||
//angular.element('#noteImportModal').modal('hide');
|
||||
} else {
|
||||
$scope.note.errorText = 'Invalid JSON';
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@
|
|||
$location.path('/notebook/' + data.note.id);
|
||||
} else if (op === 'NOTES_INFO') {
|
||||
$rootScope.$broadcast('setNoteMenu', data.notes);
|
||||
} else if (op === 'LIST_NOTEBOOK_JOBS') {
|
||||
$rootScope.$broadcast('setNotebookJobs', data.notebookJobs);
|
||||
} else if (op === 'LIST_UPDATE_NOTEBOOK_JOBS') {
|
||||
$rootScope.$broadcast('setUpdateNotebookJobs', data.notebookRunningJobs);
|
||||
} else if (op === 'LIST_NOTE_JOBS') {
|
||||
$rootScope.$broadcast('setNoteJobs', data.noteJobs);
|
||||
} else if (op === 'LIST_UPDATE_NOTE_JOBS') {
|
||||
$rootScope.$broadcast('setUpdateNoteJobs', data.noteRunningJobs);
|
||||
} else if (op === 'AUTH_INFO') {
|
||||
BootstrapDialog.show({
|
||||
closable: false,
|
||||
|
|
|
|||
|
|
@ -21,23 +21,23 @@
|
|||
function websocketMsgSrv($rootScope, websocketEvents) {
|
||||
return {
|
||||
|
||||
getHomeNotebook: function() {
|
||||
getHomeNote: function() {
|
||||
websocketEvents.sendNewEvent({op: 'GET_HOME_NOTE'});
|
||||
},
|
||||
|
||||
createNotebook: function(noteName) {
|
||||
createNote: function(noteName) {
|
||||
websocketEvents.sendNewEvent({op: 'NEW_NOTE',data: {name: noteName}});
|
||||
},
|
||||
|
||||
deleteNotebook: function(noteId) {
|
||||
deleteNote: function(noteId) {
|
||||
websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}});
|
||||
},
|
||||
|
||||
cloneNotebook: function(noteIdToClone, newNoteName) {
|
||||
cloneNote: function(noteIdToClone, newNoteName) {
|
||||
websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}});
|
||||
},
|
||||
|
||||
getNotebookList: function() {
|
||||
getNoteList: function() {
|
||||
websocketEvents.sendNewEvent({op: 'LIST_NOTES'});
|
||||
},
|
||||
|
||||
|
|
@ -45,11 +45,11 @@
|
|||
websocketEvents.sendNewEvent({op: 'RELOAD_NOTES_FROM_REPO'});
|
||||
},
|
||||
|
||||
getNotebook: function(noteId) {
|
||||
getNote: function(noteId) {
|
||||
websocketEvents.sendNewEvent({op: 'GET_NOTE', data: {id: noteId}});
|
||||
},
|
||||
|
||||
updateNotebook: function(noteId, noteName, noteConfig) {
|
||||
updateNote: function(noteId, noteName, noteConfig) {
|
||||
websocketEvents.sendNewEvent({op: 'NOTE_UPDATE', data: {id: noteId, name: noteName, config: noteConfig}});
|
||||
},
|
||||
|
||||
|
|
@ -146,18 +146,18 @@
|
|||
});
|
||||
},
|
||||
|
||||
importNotebook: function(notebook) {
|
||||
importNote: function(note) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'IMPORT_NOTE',
|
||||
data: {
|
||||
notebook: notebook
|
||||
note: note
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
checkpointNotebook: function(noteId, commitMessage) {
|
||||
checkpointNote: function(noteId, commitMessage) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'CHECKPOINT_NOTEBOOK',
|
||||
op: 'CHECKPOINT_NOTE',
|
||||
data: {
|
||||
noteId: noteId,
|
||||
commitMessage: commitMessage
|
||||
|
|
@ -198,27 +198,27 @@
|
|||
return websocketEvents.isConnected();
|
||||
},
|
||||
|
||||
getNotebookJobsList: function() {
|
||||
websocketEvents.sendNewEvent({op: 'LIST_NOTEBOOK_JOBS'});
|
||||
getNoteJobsList: function() {
|
||||
websocketEvents.sendNewEvent({op: 'LIST_NOTE_JOBS'});
|
||||
},
|
||||
|
||||
getUpdateNotebookJobsList: function(lastUpdateServerUnixTime) {
|
||||
getUpdateNoteJobsList: function(lastUpdateServerUnixTime) {
|
||||
websocketEvents.sendNewEvent(
|
||||
{op: 'LIST_UPDATE_NOTEBOOK_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
|
||||
{op: 'LIST_UPDATE_NOTE_JOBS', data: {lastUpdateUnixTime: lastUpdateServerUnixTime * 1}}
|
||||
);
|
||||
},
|
||||
|
||||
unsubscribeJobManager: function() {
|
||||
websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTEBOOK_JOBS'});
|
||||
websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_UPDATE_NOTE_JOBS'});
|
||||
},
|
||||
|
||||
getInterpreterBindings: function(noteID) {
|
||||
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteID: noteID}});
|
||||
getInterpreterBindings: function(noteId) {
|
||||
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_BINDINGS', data: {noteId: noteId}});
|
||||
},
|
||||
|
||||
saveInterpreterBindings: function(noteID, selectedSettingIds) {
|
||||
saveInterpreterBindings: function(noteId, selectedSettingIds) {
|
||||
websocketEvents.sendNewEvent({op: 'SAVE_INTERPRETER_BINDINGS',
|
||||
data: {noteID: noteID, selectedSettingIds: selectedSettingIds}});
|
||||
data: {noteId: noteId, selectedSettingIds: selectedSettingIds}});
|
||||
},
|
||||
|
||||
listConfigurations: function() {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ limitations under the License.
|
|||
<script src="bower_components/handsontable/dist/handsontable.js"></script>
|
||||
<script src="bower_components/moment-duration-format/lib/moment-duration-format.js"></script>
|
||||
<script src="bower_components/select2/dist/js/select2.js"></script>
|
||||
<script src="bower_components/angular-esri-map/dist/angular-esri-map.js"></script>
|
||||
<!-- endbower -->
|
||||
<!-- endbuild -->
|
||||
<!-- build:js({.tmp,src}) scripts/scripts.js -->
|
||||
|
|
@ -178,7 +177,7 @@ limitations under the License.
|
|||
<script src="components/noteName-create/visible.directive.js"></script>
|
||||
<script src="components/websocketEvents/websocketMsg.service.js"></script>
|
||||
<script src="components/websocketEvents/websocketEvents.factory.js"></script>
|
||||
<script src="components/notebookListDataFactory/notebookList.datafactory.js"></script>
|
||||
<script src="components/noteListDataFactory/noteList.datafactory.js"></script>
|
||||
<script src="components/baseUrl/baseUrl.service.js"></script>
|
||||
<script src="components/browser-detect/browserDetect.service.js"></script>
|
||||
<script src="components/saveAs/saveAs.service.js"></script>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ module.exports = function(config) {
|
|||
'bower_components/handsontable/dist/handsontable.js',
|
||||
'bower_components/moment-duration-format/lib/moment-duration-format.js',
|
||||
'bower_components/select2/dist/js/select2.js',
|
||||
'bower_components/angular-esri-map/dist/angular-esri-map.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
// endbower
|
||||
'src/app/app.js',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ describe('Controller: NotebookCtrl', function() {
|
|||
var scope;
|
||||
|
||||
var websocketMsgSrvMock = {
|
||||
getNotebook: function() {},
|
||||
getNote: function() {},
|
||||
listRevisionHistory: function() {},
|
||||
getInterpreterBindings: function() {}
|
||||
};
|
||||
|
|
@ -19,7 +19,7 @@ describe('Controller: NotebookCtrl', function() {
|
|||
|
||||
var noteMock = {
|
||||
id: 1,
|
||||
name: 'my notebook',
|
||||
name: 'my note',
|
||||
config: {},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ describe('Controller: NotenameCtrl', function() {
|
|||
|
||||
var scope;
|
||||
var ctrl;
|
||||
var notebookList;
|
||||
var noteList;
|
||||
|
||||
beforeEach(inject(function($injector, $rootScope, $controller) {
|
||||
notebookList = $injector.get('notebookListDataFactory');
|
||||
noteList = $injector.get('noteListDataFactory');
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('NotenameCtrl', {
|
||||
$scope: scope,
|
||||
notebookListDataFactory: notebookList
|
||||
noteListDataFactory: noteList
|
||||
});
|
||||
}));
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ describe('Controller: NotenameCtrl', function() {
|
|||
{name: 'Untitled Note 6', id: '4'}
|
||||
];
|
||||
|
||||
notebookList.setNotes(notesList);
|
||||
noteList.setNotes(notesList);
|
||||
|
||||
ctrl.sourceNoteName = 'test name';
|
||||
expect(ctrl.cloneNoteName()).toEqual('test name 1');
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ describe('Controller: ParagraphCtrl', function() {
|
|||
'getResultType', 'loadTableData', 'setGraphMode', 'isGraphMode', 'onGraphOptionChange',
|
||||
'removeGraphOptionKeys', 'removeGraphOptionValues', 'removeGraphOptionGroups', 'setGraphOptionValueAggr',
|
||||
'removeScatterOptionXaxis', 'removeScatterOptionYaxis', 'removeScatterOptionGroup',
|
||||
'removeScatterOptionSize', 'removeMapOptionLat', 'removeMapOptionLng', 'removeMapOptionPinInfo'];
|
||||
'removeScatterOptionSize'];
|
||||
|
||||
functions.forEach(function(fn) {
|
||||
it('check for scope functions to be defined : ' + fn, function() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
describe('Factory: NotebookList', function() {
|
||||
describe('Factory: NoteList', function() {
|
||||
|
||||
var notebookList;
|
||||
var noteList;
|
||||
|
||||
beforeEach(function() {
|
||||
module('zeppelinWebApp');
|
||||
|
||||
inject(function($injector) {
|
||||
notebookList = $injector.get('notebookListDataFactory');
|
||||
noteList = $injector.get('noteListDataFactory');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ describe('Factory: NotebookList', function() {
|
|||
var notesList = [
|
||||
{name: 'A', id: '000001'},
|
||||
{name: 'B', id: '000002'},
|
||||
{id: '000003'}, // notebook without name
|
||||
{id: '000003'}, // note without name
|
||||
{name: '/C/CA', id: '000004'},
|
||||
{name: '/C/CB', id: '000005'},
|
||||
{name: '/C/CB/CBA', id: '000006'}, // same name with a dir
|
||||
|
|
@ -24,9 +24,9 @@ describe('Factory: NotebookList', function() {
|
|||
{name: 'C///CB//CBB', id: '000008'},
|
||||
{name: 'D/D[A/DA]B', id: '000009'} // check if '[' and ']' considered as folder seperator
|
||||
];
|
||||
notebookList.setNotes(notesList);
|
||||
noteList.setNotes(notesList);
|
||||
|
||||
var flatList = notebookList.flatList;
|
||||
var flatList = noteList.flatList;
|
||||
expect(flatList.length).toBe(9);
|
||||
expect(flatList[0].name).toBe('A');
|
||||
expect(flatList[0].id).toBe('000001');
|
||||
|
|
@ -39,7 +39,7 @@ describe('Factory: NotebookList', function() {
|
|||
expect(flatList[7].name).toBe('C///CB//CBB');
|
||||
expect(flatList[8].name).toBe('D/D[A/DA]B');
|
||||
|
||||
var folderList = notebookList.root.children;
|
||||
var folderList = noteList.root.children;
|
||||
expect(folderList.length).toBe(5);
|
||||
expect(folderList[0].name).toBe('A');
|
||||
expect(folderList[0].id).toBe('000001');
|
||||
|
|
@ -121,6 +121,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
|
||||
private DependencyResolver depResolver;
|
||||
|
||||
private boolean shiroEnabled;
|
||||
|
||||
private Map<String, String> env = new HashMap<>();
|
||||
|
||||
private Interpreter devInterpreter;
|
||||
|
|
@ -128,18 +130,18 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
public InterpreterFactory(ZeppelinConfiguration conf,
|
||||
AngularObjectRegistryListener angularObjectRegistryListener,
|
||||
RemoteInterpreterProcessListener remoteInterpreterProcessListener,
|
||||
ApplicationEventListener appEventListener, DependencyResolver depResolver)
|
||||
throws InterpreterException, IOException, RepositoryException {
|
||||
ApplicationEventListener appEventListener, DependencyResolver depResolver,
|
||||
boolean shiroEnabled) throws InterpreterException, IOException, RepositoryException {
|
||||
this(conf, new InterpreterOption(true), angularObjectRegistryListener,
|
||||
remoteInterpreterProcessListener, appEventListener, depResolver);
|
||||
remoteInterpreterProcessListener, appEventListener, depResolver, shiroEnabled);
|
||||
}
|
||||
|
||||
|
||||
public InterpreterFactory(ZeppelinConfiguration conf, InterpreterOption defaultOption,
|
||||
AngularObjectRegistryListener angularObjectRegistryListener,
|
||||
RemoteInterpreterProcessListener remoteInterpreterProcessListener,
|
||||
ApplicationEventListener appEventListener, DependencyResolver depResolver)
|
||||
throws InterpreterException, IOException, RepositoryException {
|
||||
ApplicationEventListener appEventListener, DependencyResolver depResolver,
|
||||
boolean shiroEnabled) throws InterpreterException, IOException, RepositoryException {
|
||||
this.conf = conf;
|
||||
this.defaultOption = defaultOption;
|
||||
this.angularObjectRegistryListener = angularObjectRegistryListener;
|
||||
|
|
@ -147,6 +149,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
this.interpreterRepositories = depResolver.getRepos();
|
||||
this.remoteInterpreterProcessListener = remoteInterpreterProcessListener;
|
||||
this.appEventListener = appEventListener;
|
||||
this.shiroEnabled = shiroEnabled;
|
||||
String replsConf = conf.getString(ConfVars.ZEPPELIN_INTERPRETERS);
|
||||
interpreterClassList = replsConf.split(",");
|
||||
String groupOrder = conf.getString(ConfVars.ZEPPELIN_INTERPRETER_GROUP_ORDER);
|
||||
|
|
@ -157,6 +160,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
gson = builder.create();
|
||||
|
||||
init();
|
||||
|
||||
logger.info("shiroEnabled: {}", shiroEnabled);
|
||||
}
|
||||
|
||||
private void init() throws InterpreterException, IOException, RepositoryException {
|
||||
|
|
@ -596,6 +601,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
setting.setProperties(p);
|
||||
setting.setInterpreterGroupFactory(this);
|
||||
interpreterSettings.put(setting.getId(), setting);
|
||||
loadInterpreterDependencies(setting);
|
||||
saveToFile();
|
||||
return setting;
|
||||
}
|
||||
|
|
@ -697,16 +703,18 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
return interpreterGroup;
|
||||
}
|
||||
|
||||
public void removeInterpretersForNote(InterpreterSetting interpreterSetting, String noteId) {
|
||||
if (interpreterSetting.getOption().isPerNoteProcess()) {
|
||||
public void removeInterpretersForNote(InterpreterSetting interpreterSetting, String user,
|
||||
String noteId) {
|
||||
InterpreterOption option = interpreterSetting.getOption();
|
||||
if (option.isProcess()) {
|
||||
interpreterSetting.closeAndRemoveInterpreterGroup(noteId);
|
||||
} else if (interpreterSetting.getOption().isPerNoteSession()) {
|
||||
InterpreterGroup interpreterGroup = interpreterSetting.getInterpreterGroup(noteId);
|
||||
|
||||
interpreterGroup.close(noteId);
|
||||
interpreterGroup.destroy(noteId);
|
||||
} else if (option.isSession()) {
|
||||
InterpreterGroup interpreterGroup = interpreterSetting.getInterpreterGroup(user, noteId);
|
||||
String key = getInterpreterInstanceKey(user, noteId, interpreterSetting);
|
||||
interpreterGroup.close(key);
|
||||
interpreterGroup.destroy(key);
|
||||
synchronized (interpreterGroup) {
|
||||
interpreterGroup.remove(noteId);
|
||||
interpreterGroup.remove(key);
|
||||
interpreterGroup.notifyAll(); // notify createInterpreterForNote()
|
||||
}
|
||||
logger.info("Interpreter instance {} for note {} is removed", interpreterSetting.getName(),
|
||||
|
|
@ -714,9 +722,9 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public void createInterpretersForNote(InterpreterSetting interpreterSetting, String noteId,
|
||||
String key) {
|
||||
InterpreterGroup interpreterGroup = interpreterSetting.getInterpreterGroup(noteId);
|
||||
public void createInterpretersForNote(InterpreterSetting interpreterSetting, String user,
|
||||
String noteId, String key) {
|
||||
InterpreterGroup interpreterGroup = interpreterSetting.getInterpreterGroup(user, noteId);
|
||||
InterpreterOption option = interpreterSetting.getOption();
|
||||
Properties properties = interpreterSetting.getProperties();
|
||||
if (option.isExistingProcess) {
|
||||
|
|
@ -860,8 +868,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private void putNoteInterpreterSettingBinding(String noteId, List<String> settingList)
|
||||
throws IOException {
|
||||
private void putNoteInterpreterSettingBinding(String user, String noteId,
|
||||
List<String> settingList) throws IOException {
|
||||
List<String> unBindedSettings = new LinkedList<>();
|
||||
|
||||
synchronized (interpreterSettings) {
|
||||
|
|
@ -878,18 +886,18 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
|
||||
for (String settingId : unBindedSettings) {
|
||||
InterpreterSetting setting = get(settingId);
|
||||
removeInterpretersForNote(setting, noteId);
|
||||
removeInterpretersForNote(setting, user, noteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNoteInterpreterSettingBinding(String noteId) {
|
||||
public void removeNoteInterpreterSettingBinding(String user, String noteId) {
|
||||
synchronized (interpreterSettings) {
|
||||
List<String> settingIds = (interpreterBindings.containsKey(noteId) ?
|
||||
interpreterBindings.remove(noteId) :
|
||||
Collections.<String>emptyList());
|
||||
for (String settingId : settingIds) {
|
||||
this.removeInterpretersForNote(get(settingId), noteId);
|
||||
this.removeInterpretersForNote(get(settingId), user, noteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1092,8 +1100,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
* @param ids InterpreterSetting id list
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setInterpreters(String noteId, List<String> ids) throws IOException {
|
||||
putNoteInterpreterSettingBinding(noteId, ids);
|
||||
public void setInterpreters(String user, String noteId, List<String> ids) throws IOException {
|
||||
putNoteInterpreterSettingBinding(user, noteId, ids);
|
||||
}
|
||||
|
||||
public List<String> getInterpreters(String noteId) {
|
||||
|
|
@ -1118,7 +1126,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
return settings;
|
||||
}
|
||||
|
||||
public void closeNote(String noteId) {
|
||||
public void closeNote(String user, String noteId) {
|
||||
// close interpreters in this note session
|
||||
List<InterpreterSetting> settings = getInterpreterSettings(noteId);
|
||||
if (settings == null || settings.size() == 0) {
|
||||
|
|
@ -1127,28 +1135,37 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
|
||||
logger.info("closeNote: {}", noteId);
|
||||
for (InterpreterSetting setting : settings) {
|
||||
removeInterpretersForNote(setting, noteId);
|
||||
removeInterpretersForNote(setting, user, noteId);
|
||||
}
|
||||
}
|
||||
|
||||
private String getInterpreterInstanceKey(String noteId, InterpreterSetting setting) {
|
||||
if (setting.getOption().isExistingProcess()) {
|
||||
return Constants.EXISTING_PROCESS;
|
||||
} else if (setting.getOption().isPerNoteSession() || setting.getOption().isPerNoteProcess()) {
|
||||
return noteId;
|
||||
} else {
|
||||
return SHARED_SESSION;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Interpreter> createOrGetInterpreterList(String noteId, InterpreterSetting setting) {
|
||||
InterpreterGroup interpreterGroup = setting.getInterpreterGroup(noteId);
|
||||
synchronized (interpreterGroup) {
|
||||
String key = getInterpreterInstanceKey(noteId, setting);
|
||||
if (!interpreterGroup.containsKey(key)) {
|
||||
createInterpretersForNote(setting, noteId, key);
|
||||
private String getInterpreterInstanceKey(String user, String noteId, InterpreterSetting setting) {
|
||||
InterpreterOption option = setting.getOption();
|
||||
String key;
|
||||
if (option.isExistingProcess()) {
|
||||
key = Constants.EXISTING_PROCESS;
|
||||
} else if (!option.perNoteShared()) {
|
||||
key = noteId;
|
||||
if (shiroEnabled && !option.perUserShared()) {
|
||||
key = user + ":" + key;
|
||||
}
|
||||
return interpreterGroup.get(getInterpreterInstanceKey(noteId, setting));
|
||||
} else {
|
||||
key = SHARED_SESSION;
|
||||
}
|
||||
|
||||
logger.debug("Interpreter instance key: {}", key);
|
||||
return key;
|
||||
}
|
||||
|
||||
private List<Interpreter> createOrGetInterpreterList(String user, String noteId,
|
||||
InterpreterSetting setting) {
|
||||
InterpreterGroup interpreterGroup = setting.getInterpreterGroup(user, noteId);
|
||||
synchronized (interpreterGroup) {
|
||||
String key = getInterpreterInstanceKey(user, noteId, setting);
|
||||
if (!interpreterGroup.containsKey(key)) {
|
||||
createInterpretersForNote(setting, user, noteId, key);
|
||||
}
|
||||
return interpreterGroup.get(getInterpreterInstanceKey(user, noteId, setting));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1189,14 +1206,15 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Interpreter getInterpreter(String noteId, InterpreterSetting setting, String name) {
|
||||
private Interpreter getInterpreter(String user, String noteId, InterpreterSetting setting,
|
||||
String name) {
|
||||
Preconditions.checkNotNull(noteId, "noteId should be not null");
|
||||
Preconditions.checkNotNull(setting, "setting should be not null");
|
||||
Preconditions.checkNotNull(name, "name should be not null");
|
||||
|
||||
String className;
|
||||
if (null != (className = getInterpreterClassFromInterpreterSetting(setting, name))) {
|
||||
List<Interpreter> interpreterGroup = createOrGetInterpreterList(noteId, setting);
|
||||
List<Interpreter> interpreterGroup = createOrGetInterpreterList(user, noteId, setting);
|
||||
for (Interpreter interpreter : interpreterGroup) {
|
||||
if (className.equals(interpreter.getClassName())) {
|
||||
return interpreter;
|
||||
|
|
@ -1206,7 +1224,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
public Interpreter getInterpreter(String noteId, String replName) {
|
||||
public Interpreter getInterpreter(String user, String noteId, String replName) {
|
||||
List<InterpreterSetting> settings = getInterpreterSettings(noteId);
|
||||
InterpreterSetting setting;
|
||||
Interpreter interpreter;
|
||||
|
|
@ -1219,7 +1237,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
// get default settings (first available)
|
||||
// TODO(jl): Fix it in case of returning null
|
||||
InterpreterSetting defaultSettings = getDefaultInterpreterSetting(settings);
|
||||
return createOrGetInterpreterList(noteId, defaultSettings).get(0);
|
||||
return createOrGetInterpreterList(user, noteId, defaultSettings).get(0);
|
||||
}
|
||||
|
||||
String[] replNameSplit = replName.split("\\.");
|
||||
|
|
@ -1232,7 +1250,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
setting = getInterpreterSettingByGroup(settings, group);
|
||||
|
||||
if (null != setting) {
|
||||
interpreter = getInterpreter(noteId, setting, name);
|
||||
interpreter = getInterpreter(user, noteId, setting, name);
|
||||
|
||||
if (null != interpreter) {
|
||||
return interpreter;
|
||||
|
|
@ -1247,7 +1265,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
// TODO(jl): Handle with noteId to support defaultInterpreter per note.
|
||||
setting = getDefaultInterpreterSetting(settings);
|
||||
|
||||
interpreter = getInterpreter(noteId, setting, replName);
|
||||
interpreter = getInterpreter(user, noteId, setting, replName);
|
||||
|
||||
if (null != interpreter) {
|
||||
return interpreter;
|
||||
|
|
@ -1258,7 +1276,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
setting = getInterpreterSettingByGroup(settings, replName);
|
||||
|
||||
if (null != setting) {
|
||||
List<Interpreter> interpreters = createOrGetInterpreterList(noteId, setting);
|
||||
List<Interpreter> interpreters = createOrGetInterpreterList(user, noteId, setting);
|
||||
if (null != interpreters) {
|
||||
return interpreters.get(0);
|
||||
}
|
||||
|
|
@ -1267,7 +1285,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
// Support the legacy way to use it
|
||||
for (InterpreterSetting s : settings) {
|
||||
if (s.getGroup().equals(replName)) {
|
||||
List<Interpreter> interpreters = createOrGetInterpreterList(noteId, s);
|
||||
List<Interpreter> interpreters = createOrGetInterpreterList(user, noteId, s);
|
||||
if (null != interpreters) {
|
||||
return interpreters.get(0);
|
||||
}
|
||||
|
|
@ -1329,8 +1347,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
this.env = env;
|
||||
}
|
||||
|
||||
public Map<String, Object> getEditorSetting(String noteId, String replName) {
|
||||
Interpreter intp = getInterpreter(noteId, replName);
|
||||
public Map<String, Object> getEditorSetting(String user, String noteId, String replName) {
|
||||
Interpreter intp = getInterpreter(user, noteId, replName);
|
||||
Map<String, Object> editor = Maps.newHashMap(
|
||||
ImmutableMap.<String, Object>builder()
|
||||
.put("language", "text").build());
|
||||
|
|
|
|||
|
|
@ -17,18 +17,25 @@
|
|||
|
||||
package org.apache.zeppelin.interpreter;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InterpreterOption {
|
||||
public static final transient String SHARED = "shared";
|
||||
public static final transient String SCOPED = "scoped";
|
||||
public static final transient String ISOLATED = "isolated";
|
||||
|
||||
boolean remote;
|
||||
String host = null;
|
||||
int port = -1;
|
||||
boolean perNoteSession;
|
||||
boolean perNoteProcess;
|
||||
|
||||
|
||||
String perNote;
|
||||
String perUser;
|
||||
|
||||
boolean isExistingProcess;
|
||||
boolean setPermission;
|
||||
List<String> users;
|
||||
|
|
@ -62,11 +69,21 @@ public class InterpreterOption {
|
|||
}
|
||||
|
||||
public InterpreterOption() {
|
||||
remote = false;
|
||||
this(false);
|
||||
}
|
||||
|
||||
public InterpreterOption(boolean remote) {
|
||||
this(remote, SHARED, SHARED);
|
||||
}
|
||||
|
||||
public InterpreterOption(boolean remote, String perUser, String perNote) {
|
||||
Preconditions.checkNotNull(remote);
|
||||
Preconditions.checkNotNull(perUser);
|
||||
Preconditions.checkNotNull(perNote);
|
||||
|
||||
this.remote = remote;
|
||||
this.perUser = perUser;
|
||||
this.perNote = perNote;
|
||||
}
|
||||
|
||||
public boolean isRemote() {
|
||||
|
|
@ -77,14 +94,6 @@ public class InterpreterOption {
|
|||
this.remote = remote;
|
||||
}
|
||||
|
||||
public boolean isPerNoteSession() {
|
||||
return perNoteSession;
|
||||
}
|
||||
|
||||
public void setPerNoteSession(boolean perNoteSession) {
|
||||
this.perNoteSession = perNoteSession;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
|
@ -93,11 +102,44 @@ public class InterpreterOption {
|
|||
return port;
|
||||
}
|
||||
|
||||
public boolean isPerNoteProcess() {
|
||||
return perNoteProcess;
|
||||
|
||||
public boolean perUserShared() {
|
||||
return SHARED.equals(perUser);
|
||||
}
|
||||
|
||||
public void setPerNoteProcess(boolean perNoteProcess) {
|
||||
this.perNoteProcess = perNoteProcess;
|
||||
public boolean perUserScoped() {
|
||||
return SCOPED.equals(perUser);
|
||||
}
|
||||
|
||||
public boolean perUserIsolated() {
|
||||
return ISOLATED.equals(perUser);
|
||||
}
|
||||
|
||||
public boolean perNoteShared() {
|
||||
return SHARED.equals(perNote);
|
||||
}
|
||||
|
||||
public boolean perNoteScoped() {
|
||||
return SCOPED.equals(perNote);
|
||||
}
|
||||
|
||||
public boolean perNoteIsolated() {
|
||||
return ISOLATED.equals(perNote);
|
||||
}
|
||||
|
||||
public boolean isProcess() {
|
||||
return perUserIsolated() || perNoteIsolated();
|
||||
}
|
||||
|
||||
public boolean isSession() {
|
||||
return perUserScoped() || perNoteScoped();
|
||||
}
|
||||
|
||||
public void setPerNote(String perNote) {
|
||||
this.perNote = perNote;
|
||||
}
|
||||
|
||||
public void setPerUser(String perUser) {
|
||||
this.perUser = perUser;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,11 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.zeppelin.dep.Dependency;
|
||||
|
||||
|
|
@ -35,6 +38,7 @@ import static org.apache.zeppelin.notebook.utility.IdHashes.generateId;
|
|||
* Interpreter settings
|
||||
*/
|
||||
public class InterpreterSetting {
|
||||
private static final Logger logger = LoggerFactory.getLogger(InterpreterSetting.class);
|
||||
private static final String SHARED_PROCESS = "shared_process";
|
||||
private String id;
|
||||
private String name;
|
||||
|
|
@ -51,13 +55,19 @@ public class InterpreterSetting {
|
|||
|
||||
@Deprecated private transient InterpreterGroupFactory interpreterGroupFactory;
|
||||
|
||||
public InterpreterSetting() {
|
||||
private final transient ReentrantReadWriteLock.ReadLock interpreterGroupReadLock;
|
||||
private final transient ReentrantReadWriteLock.WriteLock interpreterGroupWriteLock;
|
||||
|
||||
public InterpreterSetting() {
|
||||
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
interpreterGroupReadLock = lock.readLock();
|
||||
interpreterGroupWriteLock = lock.writeLock();
|
||||
}
|
||||
|
||||
public InterpreterSetting(String id, String name, String group,
|
||||
List<InterpreterInfo> interpreterInfos, Properties properties, List<Dependency> dependencies,
|
||||
InterpreterOption option, String path) {
|
||||
this();
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
|
|
@ -96,40 +106,59 @@ public class InterpreterSetting {
|
|||
return group;
|
||||
}
|
||||
|
||||
private String getInterpreterProcessKey(String noteId) {
|
||||
private String getInterpreterProcessKey(String user, String noteId) {
|
||||
InterpreterOption option = getOption();
|
||||
String key;
|
||||
if (getOption().isExistingProcess) {
|
||||
return Constants.EXISTING_PROCESS;
|
||||
} else if (getOption().isPerNoteProcess()) {
|
||||
return noteId;
|
||||
key = Constants.EXISTING_PROCESS;
|
||||
} else if (getOption().isProcess()) {
|
||||
key = (option.perUserIsolated() ? user : "") + ":" + (option.perNoteIsolated() ? noteId : "");
|
||||
} else {
|
||||
return SHARED_PROCESS;
|
||||
key = SHARED_PROCESS;
|
||||
}
|
||||
|
||||
logger.debug("getInterpreterProcessKey: {}", key);
|
||||
return key;
|
||||
}
|
||||
|
||||
public InterpreterGroup getInterpreterGroup(String noteId) {
|
||||
String key = getInterpreterProcessKey(noteId);
|
||||
synchronized (interpreterGroupRef) {
|
||||
if (!interpreterGroupRef.containsKey(key)) {
|
||||
String interpreterGroupId = getId() + ":" + key;
|
||||
InterpreterGroup intpGroup =
|
||||
interpreterGroupFactory.createInterpreterGroup(interpreterGroupId, getOption());
|
||||
interpreterGroupRef.put(key, intpGroup);
|
||||
}
|
||||
public InterpreterGroup getInterpreterGroup(String user, String noteId) {
|
||||
String key = getInterpreterProcessKey(user, noteId);
|
||||
if (!interpreterGroupRef.containsKey(key)) {
|
||||
String interpreterGroupId = getId() + ":" + key;
|
||||
InterpreterGroup intpGroup =
|
||||
interpreterGroupFactory.createInterpreterGroup(interpreterGroupId, getOption());
|
||||
|
||||
interpreterGroupWriteLock.lock();
|
||||
interpreterGroupRef.put(key, intpGroup);
|
||||
interpreterGroupWriteLock.unlock();
|
||||
}
|
||||
try {
|
||||
interpreterGroupReadLock.lock();
|
||||
return interpreterGroupRef.get(key);
|
||||
} finally {
|
||||
interpreterGroupReadLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<InterpreterGroup> getAllInterpreterGroups() {
|
||||
synchronized (interpreterGroupRef) {
|
||||
try {
|
||||
interpreterGroupReadLock.lock();
|
||||
return new LinkedList<>(interpreterGroupRef.values());
|
||||
} finally {
|
||||
interpreterGroupReadLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void closeAndRemoveInterpreterGroup(String noteId) {
|
||||
String key = getInterpreterProcessKey(noteId);
|
||||
InterpreterGroup groupToRemove;
|
||||
synchronized (interpreterGroupRef) {
|
||||
groupToRemove = interpreterGroupRef.remove(key);
|
||||
String key = getInterpreterProcessKey("", noteId);
|
||||
|
||||
InterpreterGroup groupToRemove = null;
|
||||
for (String intpKey : new HashSet<>(interpreterGroupRef.keySet())) {
|
||||
if (intpKey.contains(key)) {
|
||||
interpreterGroupWriteLock.lock();
|
||||
groupToRemove = interpreterGroupRef.remove(intpKey);
|
||||
interpreterGroupWriteLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
if (groupToRemove != null) {
|
||||
|
|
@ -139,11 +168,9 @@ public class InterpreterSetting {
|
|||
}
|
||||
|
||||
void closeAndRmoveAllInterpreterGroups() {
|
||||
synchronized (interpreterGroupRef) {
|
||||
HashSet<String> groupsToRemove = new HashSet<>(interpreterGroupRef.keySet());
|
||||
for (String key : groupsToRemove) {
|
||||
closeAndRemoveInterpreterGroup(key);
|
||||
}
|
||||
HashSet<String> groupsToRemove = new HashSet<>(interpreterGroupRef.keySet());
|
||||
for (String key : groupsToRemove) {
|
||||
closeAndRemoveInterpreterGroup(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue