mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge branch 'master' into ZEPPELIN-2109
This commit is contained in:
commit
894a27e519
90 changed files with 1897 additions and 542 deletions
2
LICENSE
2
LICENSE
|
|
@ -271,7 +271,7 @@ The following components are provided under the BSD 3-Clause license. See file
|
|||
========================================================================
|
||||
BSD 2-Clause licenses
|
||||
========================================================================
|
||||
The following components are provided under the BSD 3-Clause license. See file headers and project links for details.
|
||||
The following components are provided under the BSD 2-Clause license. See file headers and project links for details.
|
||||
|
||||
(BSD 2 Clause) portions of SQLLine (http://sqlline.sourceforge.net/) - http://sqlline.sourceforge.net/#license
|
||||
jdbc/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java
|
||||
|
|
|
|||
|
|
@ -239,6 +239,12 @@
|
|||
<description>Local repository for interpreter's additional dependency loading</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.dep.localrepo</name>
|
||||
<value>local-repo</value>
|
||||
<description>Local repository for dependency loader</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.interpreters</name>
|
||||
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.python.PythonInterpreterPandasSql,org.apache.zeppelin.python.PythonCondaInterpreter,org.apache.zeppelin.python.PythonDockerInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivyPySpark3Interpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter,org.apache.zeppelin.bigquery.BigQueryInterpreter,org.apache.zeppelin.beam.BeamInterpreter,org.apache.zeppelin.pig.PigInterpreter,org.apache.zeppelin.pig.PigQueryInterpreter,org.apache.zeppelin.scio.ScioInterpreter</value>
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
<li class="title"><span><b>REST API</b><span></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-interpreter.html">Interpreter API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-notebook.html">Notebook API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-notebookRepo.html">Notebook Repository API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-credential.html">Credential API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-helium.html">Helium API</a></li>
|
||||
|
|
|
|||
BIN
docs/assets/themes/zeppelin/img/pig_zeppelin_tutorial.png
Normal file
BIN
docs/assets/themes/zeppelin/img/pig_zeppelin_tutorial.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
|
|
@ -163,8 +163,10 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
|
|||
* REST API: available REST API list in Apache Zeppelin
|
||||
* [Interpreter API](./rest-api/rest-interpreter.html)
|
||||
* [Notebook API](./rest-api/rest-notebook.html)
|
||||
* [Notebook Repository API](./rest-api/rest-notebookRepo.html)
|
||||
* [Configuration API](./rest-api/rest-configuration.html)
|
||||
* [Credential API](./rest-api/rest-credential.html)
|
||||
* [Helium API](./rest-api/rest-helium.html)
|
||||
* Security: available security support in Apache Zeppelin
|
||||
* [Authentication for NGINX](./security/authentication.html)
|
||||
* [Shiro Authentication](./security/shiroauthentication.html)
|
||||
|
|
@ -179,6 +181,8 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
|
|||
* Contribute
|
||||
* [Writing Zeppelin Interpreter](./development/writingzeppelininterpreter.html)
|
||||
* [Writing Zeppelin Application (Experimental)](./development/writingzeppelinapplication.html)
|
||||
* [Writing Zeppelin Spell (Experimental)](./development/writingzeppelinspell.html)
|
||||
* [Writing Zeppelin Visualization (Experimental)](./development/writingzeppelinvisualization.html)
|
||||
* [How to contribute (code)](./development/howtocontribute.html)
|
||||
* [How to contribute (documentation website)](./development/howtocontributewebsite.html)
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,12 @@ If both are defined, then the **environment variables** will take priority.
|
|||
<td>30000</td>
|
||||
<td>Output message from interpreter exceeding the limit will be truncated</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h6 class="properties">ZEPPELIN_DEP_LOCALREPO</h6></td>
|
||||
<td><h6 class="properties">zeppelin.dep.localrepo</h6></td>
|
||||
<td>local-repo</td>
|
||||
<td>Local repository for dependency loader.<br>ex)visualiztion modules of npm.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h6 class="properties">ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE</h6></td>
|
||||
<td><h6 class="properties">zeppelin.websocket.max.text.message.size</h6></td>
|
||||
|
|
|
|||
|
|
@ -56,3 +56,8 @@ So, copying `notebook` and `conf` directory should be enough.
|
|||
- From 0.7, we uses `pegdown` as the `markdown.parser.type` option for the `%md` interpreter. Rendered markdown might be different from what you expected
|
||||
- From 0.7 note.json format has been changed to support multiple outputs in a paragraph. Zeppelin will automatically convert old format to new format. 0.6 or lower version can read new note.json format but output will not be displayed. For the detail, see [ZEPPELIN-212](http://issues.apache.org/jira/browse/ZEPPELIN-212) and [pull request](https://github.com/apache/zeppelin/pull/1658).
|
||||
- From 0.7 note storage layer will utilize `GitNotebookRepo` by default instead of `VFSNotebookRepo` storage layer, which is an extension of latter one with versioning capabilities on top of it.
|
||||
|
||||
### Upgrading from Zeppelin 0.7 to 0.8
|
||||
|
||||
- From 0.8, we recommend to use `PYSPARK_PYTHON` and `PYSPARK_DRIVER_PYTHON` instead of `zeppelin.pyspark.python` as `zeppelin.pyspark.python` only effects driver. You can use `PYSPARK_PYTHON` and `PYSPARK_DRIVER_PYTHON` as using them in spark.
|
||||
|
||||
|
|
@ -15,14 +15,16 @@ group: manual
|
|||
[Apache Pig](https://pig.apache.org/) is a platform for analyzing large data sets that consists of a high-level language for expressing data analysis programs, coupled with infrastructure for evaluating these programs. The salient property of Pig programs is that their structure is amenable to substantial parallelization, which in turns enables them to handle very large data sets.
|
||||
|
||||
## Supported interpreter type
|
||||
- `%pig.script` (default)
|
||||
- `%pig.script` (default Pig interpreter, so you can use `%pig`)
|
||||
|
||||
All the pig script can run in this type of interpreter, and display type is plain text.
|
||||
`%pig.script` is like the Pig grunt shell. Anything you can run in Pig grunt shell can be run in `%pig.script` interpreter, it is used for running Pig script where you don’t need to visualize the data, it is suitable for data munging.
|
||||
|
||||
- `%pig.query`
|
||||
|
||||
Almost the same as `%pig.script`. The only difference is that you don't need to add alias in the last statement. And the display type is table.
|
||||
|
||||
`%pig.query` is a little different compared with `%pig.script`. It is used for exploratory data analysis via Pig latin where you can leverage Zeppelin’s visualization ability. There're 2 minor differences in the last statement between `%pig.script` and `%pig.query`
|
||||
- No pig alias in the last statement in `%pig.query` (read the examples below).
|
||||
- The last statement must be in single line in `%pig.query`
|
||||
|
||||
## Supported runtime mode
|
||||
- Local
|
||||
- MapReduce
|
||||
|
|
@ -52,8 +54,8 @@ group: manual
|
|||
### How to configure interpreter
|
||||
|
||||
At the Interpreters menu, you have to create a new Pig interpreter. Pig interpreter has below properties by default.
|
||||
And you can set any pig properties here which will be passed to pig engine. (like tez.queue.name & mapred.job.queue.name).
|
||||
Besides, we use paragraph title as job name if it exists, else use the last line of pig script. So you can use that to find app running in YARN RM UI.
|
||||
And you can set any Pig properties here which will be passed to Pig engine. (like tez.queue.name & mapred.job.queue.name).
|
||||
Besides, we use paragraph title as job name if it exists, else use the last line of Pig script. So you can use that to find app running in YARN RM UI.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -95,22 +97,52 @@ Besides, we use paragraph title as job name if it exists, else use the last line
|
|||
```
|
||||
%pig
|
||||
|
||||
raw_data = load 'dataset/sf_crime/train.csv' using PigStorage(',') as (Dates,Category,Descript,DayOfWeek,PdDistrict,Resolution,Address,X,Y);
|
||||
b = group raw_data all;
|
||||
c = foreach b generate COUNT($1);
|
||||
dump c;
|
||||
bankText = load 'bank.csv' using PigStorage(';');
|
||||
bank = foreach bankText generate $0 as age, $1 as job, $2 as marital, $3 as education, $5 as balance;
|
||||
bank = filter bank by age != '"age"';
|
||||
bank = foreach bank generate (int)age, REPLACE(job,'"','') as job, REPLACE(marital, '"', '') as marital, (int)(REPLACE(balance, '"', '')) as balance;
|
||||
store bank into 'clean_bank.csv' using PigStorage(';'); -- this statement is optional, it just show you that most of time %pig.script is used for data munging before querying the data.
|
||||
```
|
||||
|
||||
##### pig.query
|
||||
|
||||
Get the number of each age where age is less than 30
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
b = foreach raw_data generate Category;
|
||||
c = group b by Category;
|
||||
foreach c generate group as category, COUNT($1) as count;
|
||||
|
||||
bank_data = filter bank by age < 30;
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1);
|
||||
```
|
||||
|
||||
The same as above, but use dynamic text form so that use can specify the variable maxAge in textbox. (See screenshot below). Dynamic form is a very cool feature of Zeppelin, you can refer this [link]((../manual/dynamicform.html)) for details.
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
bank_data = filter bank by age < ${maxAge=40};
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1) as count;
|
||||
```
|
||||
|
||||
Get the number of each age for specific marital type, also use dynamic form here. User can choose the marital type in the dropdown list (see screenshot below).
|
||||
|
||||
```
|
||||
%pig.query
|
||||
|
||||
bank_data = filter bank by marital=='${marital=single,single|divorced|married}';
|
||||
b = group bank_data by age;
|
||||
foreach b generate group, COUNT($1) as count;
|
||||
```
|
||||
|
||||
The above examples are in the Pig tutorial note in Zeppelin, you can check that for details. Here's the screenshot.
|
||||
|
||||
<img class="img-responsive" width="1024px" style="margin:0 auto; padding: 26px;" src="../assets/themes/zeppelin/img/pig_zeppelin_tutorial.png" />
|
||||
|
||||
|
||||
Data is shared between `%pig` and `%pig.query`, so that you can do some common work in `%pig`, and do different kinds of query based on the data of `%pig`.
|
||||
Besides, we recommend you to specify alias explicitly so that the visualization can display the column name correctly. Here, we name `COUNT($1)` as `count`, if you don't do this,
|
||||
then we will name it using position, here we will use `col_1` to represent `COUNT($1)` if you don't specify alias for it. There's one pig tutorial note in zeppelin for your reference.
|
||||
Besides, we recommend you to specify alias explicitly so that the visualization can display the column name correctly. In the above example 2 and 3 of `%pig.query`, we name `COUNT($1)` as `count`. If you don't do this,
|
||||
then we will name it using position. E.g. in the above first example of `%pig.query`, we will use `col_1` in chart to represent `COUNT($1)`.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -104,9 +104,16 @@ You can also set other Spark properties which are not listed in the table. For a
|
|||
<td>Local repository for dependency loader</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.pyspark.python</td>
|
||||
<td>PYSPARK_PYTHON</td>
|
||||
<td>python</td>
|
||||
<td>Python command to run pyspark with</td>
|
||||
<td>Python binary executable to use for PySpark in both driver and workers (default is <code>python</code>).
|
||||
Property <code>spark.pyspark.python</code> take precedence if it is set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PYSPARK_DRIVER_PYTHON</td>
|
||||
<td>python</td>
|
||||
<td>Python binary executable to use for PySpark in driver only (default is <code>PYSPARK_PYTHON</code>).
|
||||
Property <code>spark.pyspark.driver.python</code> take precedence if it is set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.spark.concurrentSQL</td>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
|
|||
"enabled": false
|
||||
}
|
||||
],
|
||||
"zeppelin_horizontalbar": [
|
||||
"zeppelin\_horizontalbar": [
|
||||
{
|
||||
"registry": "local",
|
||||
"pkg": {
|
||||
|
|
|
|||
179
docs/rest-api/rest-notebookRepo.md
Normal file
179
docs/rest-api/rest-notebookRepo.md
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Apache Zeppelin notebook repository REST API"
|
||||
description: "This page contains Apache Zeppelin notebook repository REST API information."
|
||||
group: rest-api
|
||||
---
|
||||
<!--
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
# Apache Zeppelin Notebook Repository API
|
||||
|
||||
<div id="toc"></div>
|
||||
|
||||
## Overview
|
||||
Apache Zeppelin provides several REST APIs for interaction and remote activation of zeppelin functionality.
|
||||
All REST APIs are available starting with the following endpoint `http://[zeppelin-server]:[zeppelin-port]/api`.
|
||||
Note that Apache Zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewers such as [JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc).
|
||||
|
||||
If you work with Apache Zeppelin and find a need for an additional REST API, please [file an issue or send us an email](http://zeppelin.apache.org/community.html).
|
||||
|
||||
## Notebook Repository REST API List
|
||||
|
||||
### List all available notebook repositories
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method returns all the available notebook repositories.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>500</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": "",
|
||||
"body": [
|
||||
{
|
||||
"name": "GitNotebookRepo",
|
||||
"className": "org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings": [
|
||||
{
|
||||
"type": "INPUT",
|
||||
"value": [],
|
||||
"selected": "ZEPPELIN_HOME/zeppelin/notebook/",
|
||||
"name": "Notebook Path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
||||
### Reload a notebook repository
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method triggers reloading and broadcasting of the note list.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories/reload```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>500</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": ""
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
||||
### Update a specific notebook repository
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```PUT``` method updates a specific notebook repository.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook-repositories```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td>
|
||||
404 when the specified notebook repository doesn't exist <br/>
|
||||
406 for invalid payload <br/>
|
||||
500 for any other errors
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON input</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"name":"org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings":{
|
||||
"Notebook Path":"/tmp/notebook/"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status": "OK",
|
||||
"message": "",
|
||||
"body": {
|
||||
"name": "GitNotebookRepo",
|
||||
"className": "org.apache.zeppelin.notebook.repo.GitNotebookRepo",
|
||||
"settings": [
|
||||
{
|
||||
"type": "INPUT",
|
||||
"value": [],
|
||||
"selected": "/tmp/notebook/",
|
||||
"name": "Notebook Path"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -83,7 +83,7 @@ public class ElasticsearchInterpreter extends Interpreter {
|
|||
+ " . same comments as for the search\n"
|
||||
+ " - get /index/type/id\n"
|
||||
+ " - delete /index/type/id\n"
|
||||
+ " - index /ndex/type/id <json-formatted document>\n"
|
||||
+ " - index /index/type/id <json-formatted document>\n"
|
||||
+ " . the id can be omitted, elasticsearch will generate one";
|
||||
|
||||
protected static final List<String> COMMANDS = Arrays.asList(
|
||||
|
|
|
|||
|
|
@ -33,13 +33,6 @@ import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
|||
* Dummy interpreter to support development mode for Zeppelin app
|
||||
*/
|
||||
public class DevInterpreter extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"dev",
|
||||
"dev",
|
||||
DevInterpreter.class.getName(),
|
||||
new InterpreterPropertyBuilder().build());
|
||||
}
|
||||
|
||||
private InterpreterEvent interpreterEvent;
|
||||
private InterpreterContext context;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,3 @@
|
|||
/**
|
||||
* 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.jdbc;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@
|
|||
{
|
||||
"text": "%pig\n\nbankText \u003d load \u0027bank.csv\u0027 using PigStorage(\u0027;\u0027);\nbank \u003d foreach bankText generate $0 as age, $1 as job, $2 as marital, $3 as education, $5 as balance; \nbank \u003d filter bank by age !\u003d \u0027\"age\"\u0027;\nbank \u003d foreach bank generate (int)age, REPLACE(job,\u0027\"\u0027,\u0027\u0027) as job, REPLACE(marital, \u0027\"\u0027, \u0027\u0027) as marital, (int)(REPLACE(balance, \u0027\"\u0027, \u0027\u0027)) as balance;\n\n-- The following statement is optional, it depends on whether your needs.\n-- store bank into \u0027clean_bank.csv\u0027 using PigStorage(\u0027;\u0027);\n\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:11 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:08 PM",
|
||||
"config": {
|
||||
"colWidth": 12.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -138,15 +138,15 @@
|
|||
"jobName": "paragraph_1483277250237_-466604517",
|
||||
"id": "20161228-140640_1560978333",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:11 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:13 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:08 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:11 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c 30;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1);\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:16 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:13 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -183,15 +183,15 @@
|
|||
"jobName": "paragraph_1483277250238_-465450270",
|
||||
"id": "20161228-140730_1903342877",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:16 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:30 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:13 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:26 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c ${maxAge\u003d40};\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1);",
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by age \u003c ${maxAge\u003d40};\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as count;",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:18 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:14 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -228,7 +228,7 @@
|
|||
"msg": [
|
||||
{
|
||||
"type": "TABLE",
|
||||
"data": "group\tcol_1\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n30\t150\n31\t199\n32\t224\n33\t186\n34\t231\n35\t180\n"
|
||||
"data": "group\tcount\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n30\t150\n31\t199\n32\t224\n33\t186\n34\t231\n35\t180\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -236,15 +236,15 @@
|
|||
"jobName": "paragraph_1483277250239_-465835019",
|
||||
"id": "20161228-154918_1551591203",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:18 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:32 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:14 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:29 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
{
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by marital\u003d\u003d\u0027${marital\u003dsingle,single|divorced|married}\u0027;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as c;\n\n\n",
|
||||
"text": "%pig.query\n\nbank_data \u003d filter bank by marital\u003d\u003d\u0027${marital\u003dsingle,single|divorced|married}\u0027;\nb \u003d group bank_data by age;\nforeach b generate group, COUNT($1) as count;\n\n\n",
|
||||
"user": "anonymous",
|
||||
"dateUpdated": "Jan 22, 2017 12:49:20 PM",
|
||||
"dateUpdated": "Feb 24, 2017 5:08:15 PM",
|
||||
"config": {
|
||||
"colWidth": 4.0,
|
||||
"editorMode": "ace/mode/pig",
|
||||
|
|
@ -292,7 +292,7 @@
|
|||
"msg": [
|
||||
{
|
||||
"type": "TABLE",
|
||||
"data": "group\tc\n23\t3\n24\t11\n25\t11\n26\t18\n27\t26\n28\t23\n29\t37\n30\t56\n31\t104\n32\t105\n33\t103\n34\t142\n35\t109\n36\t117\n37\t100\n38\t99\n39\t88\n40\t105\n41\t97\n42\t91\n43\t79\n44\t68\n45\t76\n46\t82\n47\t78\n48\t91\n49\t87\n50\t74\n51\t63\n52\t66\n53\t75\n54\t56\n55\t68\n56\t50\n57\t78\n58\t67\n59\t56\n60\t36\n61\t15\n62\t5\n63\t7\n64\t6\n65\t4\n66\t7\n67\t5\n68\t1\n69\t5\n70\t5\n71\t5\n72\t4\n73\t6\n74\t2\n75\t3\n76\t1\n77\t5\n78\t2\n79\t3\n80\t6\n81\t1\n83\t2\n86\t1\n87\t1\n"
|
||||
"data": "group\tcount\n23\t3\n24\t11\n25\t11\n26\t18\n27\t26\n28\t23\n29\t37\n30\t56\n31\t104\n32\t105\n33\t103\n34\t142\n35\t109\n36\t117\n37\t100\n38\t99\n39\t88\n40\t105\n41\t97\n42\t91\n43\t79\n44\t68\n45\t76\n46\t82\n47\t78\n48\t91\n49\t87\n50\t74\n51\t63\n52\t66\n53\t75\n54\t56\n55\t68\n56\t50\n57\t78\n58\t67\n59\t56\n60\t36\n61\t15\n62\t5\n63\t7\n64\t6\n65\t4\n66\t7\n67\t5\n68\t1\n69\t5\n70\t5\n71\t5\n72\t4\n73\t6\n74\t2\n75\t3\n76\t1\n77\t5\n78\t2\n79\t3\n80\t6\n81\t1\n83\t2\n86\t1\n87\t1\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -300,8 +300,8 @@
|
|||
"jobName": "paragraph_1483277250240_-480070728",
|
||||
"id": "20161228-142259_575675591",
|
||||
"dateCreated": "Jan 1, 2017 9:27:30 PM",
|
||||
"dateStarted": "Jan 22, 2017 12:49:30 PM",
|
||||
"dateFinished": "Jan 22, 2017 12:49:34 PM",
|
||||
"dateStarted": "Feb 24, 2017 5:08:27 PM",
|
||||
"dateFinished": "Feb 24, 2017 5:08:31 PM",
|
||||
"status": "FINISHED",
|
||||
"progressUpdateIntervalMs": 500
|
||||
},
|
||||
|
|
|
|||
3
pom.xml
3
pom.xml
|
|
@ -899,6 +899,9 @@
|
|||
<exclude>**/constants.json</exclude>
|
||||
<exclude>scripts/**</exclude>
|
||||
|
||||
<!-- from SQLLine 1.0.2, see ZEPPELIN-2135 -->
|
||||
<exclude>**/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java</exclude>
|
||||
|
||||
<!-- bundled from bootstrap -->
|
||||
<exclude>docs/assets/themes/zeppelin/bootstrap/**</exclude>
|
||||
<exclude>docs/assets/themes/zeppelin/css/style.css</exclude>
|
||||
|
|
|
|||
|
|
@ -200,7 +200,16 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
|
|||
gatewayServer.start();
|
||||
|
||||
// Run python shell
|
||||
CommandLine cmd = CommandLine.parse(getProperty("zeppelin.pyspark.python"));
|
||||
// Choose python in the order of
|
||||
// PYSPARK_DRIVER_PYTHON > PYSPARK_PYTHON > zeppelin.pyspark.python
|
||||
String pythonExec = getProperty("zeppelin.pyspark.python");
|
||||
if (System.getenv("PYSPARK_PYTHON") != null) {
|
||||
pythonExec = System.getenv("PYSPARK_PYTHON");
|
||||
}
|
||||
if (System.getenv("PYSPARK_DRIVER_PYTHON") != null) {
|
||||
pythonExec = System.getenv("PYSPARK_DRIVER_PYTHON");
|
||||
}
|
||||
CommandLine cmd = CommandLine.parse(pythonExec);
|
||||
cmd.addArgument(scriptPath, false);
|
||||
cmd.addArgument(Integer.toString(port), false);
|
||||
cmd.addArgument(Integer.toString(getSparkInterpreter().getSparkVersion().toNumber()), false);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,28 @@ export default class EchoSpell extends SpellBase {
|
|||
super("%echo");
|
||||
}
|
||||
|
||||
interpret(paragraphText) {
|
||||
return new SpellResult(paragraphText);
|
||||
/**
|
||||
* Consumes text and return `SpellResult`.
|
||||
*
|
||||
* @param paragraphText {string} which doesn't include magic
|
||||
* @param config {Object}
|
||||
* @return {SpellResult}
|
||||
*/
|
||||
interpret(paragraphText, config) {
|
||||
let repeat = 1;
|
||||
|
||||
try {
|
||||
repeat = parseFloat(config.repeat);
|
||||
} catch (error) {
|
||||
/** ignore, use default value */
|
||||
}
|
||||
|
||||
let repeated = "";
|
||||
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
repeated += `${paragraphText}\n`;
|
||||
}
|
||||
|
||||
return new SpellResult(repeated);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@
|
|||
"artifact" : "./zeppelin-examples/zeppelin-example-spell-echo",
|
||||
"license" : "Apache-2.0",
|
||||
"icon" : "<i class='fa fa-repeat'></i>",
|
||||
"config": {
|
||||
"repeat": {
|
||||
"type": "number",
|
||||
"description": "How many times to repeat",
|
||||
"defaultValue": 1
|
||||
}
|
||||
},
|
||||
"spell": {
|
||||
"magic": "%echo",
|
||||
"usage": "%echo <TEXT>"
|
||||
|
|
|
|||
|
|
@ -28,11 +28,18 @@ export default class TranslatorSpell extends SpellBase {
|
|||
super("%translator");
|
||||
}
|
||||
|
||||
interpret(paragraphText) {
|
||||
/**
|
||||
* Consumes text and return `SpellResult`.
|
||||
*
|
||||
* @param paragraphText {string} which doesn't include magic
|
||||
* @param config {Object}
|
||||
* @return {SpellResult}
|
||||
*/
|
||||
interpret(paragraphText, config) {
|
||||
const parsed = this.parseConfig(paragraphText);
|
||||
const auth = config['access-token'];
|
||||
const source = parsed.source;
|
||||
const target = parsed.target;
|
||||
const auth = parsed.auth;
|
||||
const text = parsed.text;
|
||||
|
||||
/**
|
||||
|
|
@ -49,7 +56,7 @@ export default class TranslatorSpell extends SpellBase {
|
|||
}
|
||||
|
||||
parseConfig(text) {
|
||||
const pattern = /^\s*(\S+)-(\S+)\s*(\S+)([\S\s]*)/g;
|
||||
const pattern = /^\s*(\S+)-(\S+)\s*([\S\s]*)/g;
|
||||
const match = pattern.exec(text);
|
||||
|
||||
if (!match) {
|
||||
|
|
@ -59,8 +66,7 @@ export default class TranslatorSpell extends SpellBase {
|
|||
return {
|
||||
source: match[1],
|
||||
target: match[2],
|
||||
auth: match[3],
|
||||
text: match[4],
|
||||
text: match[3],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@
|
|||
"artifact" : "./zeppelin-examples/zeppelin-example-spell-translator",
|
||||
"license" : "Apache-2.0",
|
||||
"icon" : "<i class='fa fa-globe '></i>",
|
||||
"config": {
|
||||
"access-token": {
|
||||
"type": "string",
|
||||
"description": "access token for Google Translation API",
|
||||
"defaultValue": "EXAMPLE-TOKEN"
|
||||
}
|
||||
},
|
||||
"spell": {
|
||||
"magic": "%translator",
|
||||
"usage": "%translator <source>-<target> <access-key> <TEXT>"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package org.apache.zeppelin.helium;
|
|||
|
||||
import org.apache.zeppelin.annotation.Experimental;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helium package definition
|
||||
*/
|
||||
|
|
@ -33,7 +35,8 @@ public class HeliumPackage {
|
|||
private String license;
|
||||
private String icon;
|
||||
|
||||
public SpellPackageInfo spell;
|
||||
private SpellPackageInfo spell;
|
||||
private Map<String, Object> config;
|
||||
|
||||
public HeliumPackage(HeliumType type,
|
||||
String name,
|
||||
|
|
@ -100,6 +103,7 @@ public class HeliumPackage {
|
|||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
|
@ -107,4 +111,6 @@ public class HeliumPackage {
|
|||
public SpellPackageInfo getSpellInfo() {
|
||||
return spell;
|
||||
}
|
||||
|
||||
public Map<String, Object> getConfig() { return config; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,25 +394,12 @@ public abstract class Interpreter {
|
|||
public static Map<String, RegisteredInterpreter> registeredInterpreters = Collections
|
||||
.synchronizedMap(new HashMap<String, RegisteredInterpreter>());
|
||||
|
||||
public static void register(String name, String className) {
|
||||
register(name, name, className);
|
||||
}
|
||||
|
||||
public static void register(String name, String group, String className) {
|
||||
register(name, group, className, false, new HashMap<String, InterpreterProperty>());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void register(String name, String group, String className,
|
||||
Map<String, InterpreterProperty> properties) {
|
||||
register(name, group, className, false, properties);
|
||||
}
|
||||
|
||||
public static void register(String name, String group, String className,
|
||||
boolean defaultInterpreter) {
|
||||
register(name, group, className, defaultInterpreter,
|
||||
new HashMap<String, InterpreterProperty>());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void register(String name, String group, String className,
|
||||
boolean defaultInterpreter, Map<String, InterpreterProperty> properties) {
|
||||
|
|
@ -422,6 +409,7 @@ public abstract class Interpreter {
|
|||
register(new RegisteredInterpreter(name, group, className, defaultInterpreter, properties));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void register(RegisteredInterpreter registeredInterpreter) {
|
||||
String interpreterKey = registeredInterpreter.getInterpreterKey();
|
||||
if (!registeredInterpreters.containsKey(interpreterKey)) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ package org.apache.zeppelin.helium;
|
|||
import com.google.gson.Gson;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class HeliumPackageTest {
|
||||
|
|
@ -28,7 +30,7 @@ public class HeliumPackageTest {
|
|||
|
||||
@Test
|
||||
public void parseSpellPackageInfo() {
|
||||
String exampleSpell = "{\n" +
|
||||
String examplePackage = "{\n" +
|
||||
" \"type\" : \"SPELL\",\n" +
|
||||
" \"name\" : \"echo-spell\",\n" +
|
||||
" \"description\" : \"'%echo' - return just what receive (example)\",\n" +
|
||||
|
|
@ -41,8 +43,41 @@ public class HeliumPackageTest {
|
|||
" }\n" +
|
||||
"}";
|
||||
|
||||
HeliumPackage p = gson.fromJson(exampleSpell, HeliumPackage.class);
|
||||
HeliumPackage p = gson.fromJson(examplePackage, HeliumPackage.class);
|
||||
assertEquals(p.getSpellInfo().getMagic(), "%echo");
|
||||
assertEquals(p.getSpellInfo().getUsage(), "%echo <TEXT>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseConfig() {
|
||||
String examplePackage = "{\n" +
|
||||
" \"type\" : \"SPELL\",\n" +
|
||||
" \"name\" : \"translator-spell\",\n" +
|
||||
" \"description\" : \"Translate langauges using Google API (examaple)\",\n" +
|
||||
" \"artifact\" : \"./zeppelin-examples/zeppelin-example-spell-translator\",\n" +
|
||||
" \"license\" : \"Apache-2.0\",\n" +
|
||||
" \"icon\" : \"<i class='fa fa-globe '></i>\",\n" +
|
||||
" \"config\": {\n" +
|
||||
" \"access-token\": {\n" +
|
||||
" \"type\": \"string\",\n" +
|
||||
" \"description\": \"access token for Google Translation API\",\n" +
|
||||
" \"defaultValue\": \"EXAMPLE-TOKEN\"\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"spell\": {\n" +
|
||||
" \"magic\": \"%translator\",\n" +
|
||||
" \"usage\": \"%translator <source>-<target> <access-key> <TEXT>\"\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
HeliumPackage p = gson.fromJson(examplePackage, HeliumPackage.class);
|
||||
Map<String, Object> config = p.getConfig();
|
||||
Map<String, Object> accessToken = (Map<String, Object>) config.get("access-token");
|
||||
|
||||
assertEquals((String) accessToken.get("type"),"string");
|
||||
assertEquals((String) accessToken.get("description"),
|
||||
"access token for Google Translation API");
|
||||
assertEquals((String) accessToken.get("defaultValue"),
|
||||
"EXAMPLE-TOKEN");
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ public class InterpreterTest {
|
|||
@Test
|
||||
public void testDefaultProperty() {
|
||||
Properties p = new Properties();
|
||||
p.put("p1", "v1");
|
||||
MockInterpreterA intp = new MockInterpreterA(p);
|
||||
|
||||
assertEquals(1, intp.getProperty().size());
|
||||
|
|
@ -37,27 +38,17 @@ public class InterpreterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testOverridedProperty() {
|
||||
public void testOverriddenProperty() {
|
||||
Properties p = new Properties();
|
||||
p.put("p1", "v2");
|
||||
p.put("p1", "v1");
|
||||
MockInterpreterA intp = new MockInterpreterA(p);
|
||||
Properties overriddenProperty = new Properties();
|
||||
overriddenProperty.put("p1", "v2");
|
||||
intp.setProperty(overriddenProperty);
|
||||
|
||||
assertEquals(1, intp.getProperty().size());
|
||||
assertEquals("v2", intp.getProperty().get("p1"));
|
||||
assertEquals("v2", intp.getProperty("p1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdditionalProperty() {
|
||||
Properties p = new Properties();
|
||||
p.put("p2", "v2");
|
||||
MockInterpreterA intp = new MockInterpreterA(p);
|
||||
|
||||
assertEquals(2, intp.getProperty().size());
|
||||
assertEquals("v1", intp.getProperty().get("p1"));
|
||||
assertEquals("v1", intp.getProperty("p1"));
|
||||
assertEquals("v2", intp.getProperty().get("p2"));
|
||||
assertEquals("v2", intp.getProperty("p2"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,15 +31,6 @@ import org.apache.zeppelin.scheduler.Scheduler;
|
|||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
|
||||
public class MockInterpreterA extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"interpreterA",
|
||||
"group1",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add("p1", "v1", "property1").build());
|
||||
|
||||
}
|
||||
|
||||
private String lastSt;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,15 +31,6 @@ import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
|||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
|
||||
public class MockInterpreterAngular extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"angularTest",
|
||||
"angular",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add("p1", "v1", "property1").build());
|
||||
|
||||
}
|
||||
|
||||
AtomicInteger numWatch = new AtomicInteger(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,15 +32,7 @@ import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
|||
import org.apache.zeppelin.scheduler.Scheduler;
|
||||
|
||||
public class MockInterpreterB extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"interpreterB",
|
||||
"group1",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add("p1", "v1", "property1").build());
|
||||
|
||||
}
|
||||
public MockInterpreterB(Properties property) {
|
||||
super(property);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,15 +26,6 @@ import java.util.Properties;
|
|||
|
||||
|
||||
public class MockInterpreterEnv extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"interpreterA",
|
||||
"group1",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder().build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public MockInterpreterEnv(Properties property) {
|
||||
super(property);
|
||||
|
|
|
|||
|
|
@ -29,15 +29,6 @@ import java.util.Properties;
|
|||
* MockInterpreter to test outputstream
|
||||
*/
|
||||
public class MockInterpreterOutputStream extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"interpreterOutputStream",
|
||||
"group1",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder().build());
|
||||
|
||||
}
|
||||
|
||||
private String lastSt;
|
||||
|
||||
public MockInterpreterOutputStream(Properties property) {
|
||||
|
|
|
|||
|
|
@ -34,15 +34,6 @@ import org.apache.zeppelin.resource.Resource;
|
|||
import org.apache.zeppelin.resource.ResourcePool;
|
||||
|
||||
public class MockInterpreterResourcePool extends Interpreter {
|
||||
static {
|
||||
Interpreter.register(
|
||||
"resourcePoolTest",
|
||||
"resourcePool",
|
||||
MockInterpreterA.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add("p1", "v1", "property1").build());
|
||||
|
||||
}
|
||||
|
||||
AtomicInteger numWatch = new AtomicInteger(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@
|
|||
package org.apache.zeppelin.rest;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.helium.Helium;
|
||||
import org.apache.zeppelin.helium.HeliumPackage;
|
||||
import org.apache.zeppelin.notebook.Note;
|
||||
|
|
@ -34,6 +36,7 @@ import javax.ws.rs.core.Response;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helium Rest Api
|
||||
|
|
@ -56,13 +59,34 @@ public class HeliumRestApi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all packages
|
||||
* @return
|
||||
* Get all package infos
|
||||
*/
|
||||
@GET
|
||||
@Path("all")
|
||||
public Response getAll() {
|
||||
return new JsonResponse(Response.Status.OK, "", helium.getAllPackageInfo()).build();
|
||||
@Path("package")
|
||||
public Response getAllPackageInfo() {
|
||||
return new JsonResponse(
|
||||
Response.Status.OK, "", helium.getAllPackageInfo()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single package info
|
||||
*/
|
||||
@GET
|
||||
@Path("package/{packageName}")
|
||||
public Response getSinglePackageInfo(@PathParam("packageName") String packageName) {
|
||||
if (StringUtils.isEmpty(packageName)) {
|
||||
return new JsonResponse(
|
||||
Response.Status.BAD_REQUEST,
|
||||
"Can't get package info for empty name").build();
|
||||
}
|
||||
|
||||
try {
|
||||
return new JsonResponse(
|
||||
Response.Status.OK, "", helium.getSinglePackageInfo(packageName)).build();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
|
|
@ -165,6 +189,98 @@ public class HeliumRestApi {
|
|||
return new JsonResponse(Response.Status.OK, order).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("spell/config/{packageName}")
|
||||
public Response getSpellConfigUsingMagic(@PathParam("packageName") String packageName) {
|
||||
if (StringUtils.isEmpty(packageName)) {
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
"packageName is empty" ).build();
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, Map<String, Object>> config =
|
||||
helium.getSpellConfig(packageName);
|
||||
|
||||
if (config == null) {
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
"Failed to find enabled package for " + packageName).build();
|
||||
}
|
||||
|
||||
return new JsonResponse(Response.Status.OK, config).build();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("config")
|
||||
public Response getAllPackageConfigs() {
|
||||
try {
|
||||
Map<String, Map<String, Object>> config = helium.getAllPackageConfig();
|
||||
return new JsonResponse(Response.Status.OK, config).build();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("config/{packageName}/{artifact}")
|
||||
public Response getPackageConfig(@PathParam("packageName") String packageName,
|
||||
@PathParam("artifact") String artifact) {
|
||||
if (StringUtils.isEmpty(packageName) || StringUtils.isEmpty(artifact)) {
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
"package name or artifact is empty"
|
||||
).build();
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, Map<String, Object>> config =
|
||||
helium.getPackageConfig(packageName, artifact);
|
||||
|
||||
if (config == null) {
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
"Failed to find package for " + artifact).build();
|
||||
}
|
||||
|
||||
return new JsonResponse(Response.Status.OK, config).build();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("config/{packageName}/{artifact}")
|
||||
public Response updatePackageConfig(@PathParam("packageName") String packageName,
|
||||
@PathParam("artifact") String artifact,
|
||||
String rawConfig) {
|
||||
|
||||
if (StringUtils.isEmpty(packageName) || StringUtils.isEmpty(artifact)) {
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
"package name or artifact is empty"
|
||||
).build();
|
||||
}
|
||||
|
||||
Map<String, Object> packageConfig = null;
|
||||
|
||||
try {
|
||||
packageConfig = gson.fromJson(
|
||||
rawConfig, new TypeToken<Map<String, Object>>(){}.getType());
|
||||
helium.updatePackageConfig(artifact, packageConfig);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return new JsonResponse(Response.Status.BAD_REQUEST,
|
||||
e.getMessage()).build();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR,
|
||||
e.getMessage()).build();
|
||||
}
|
||||
|
||||
return new JsonResponse(Response.Status.OK, packageConfig).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("order/visualization")
|
||||
public Response getVisualizationPackageOrder(String orderedPackageNameList) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,19 @@ public class NotebookRepoRestApi {
|
|||
return new JsonResponse<>(Status.OK, "", settings).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload notebook repository
|
||||
*/
|
||||
@GET
|
||||
@Path("reload")
|
||||
@ZeppelinApi
|
||||
public Response refreshRepo(){
|
||||
AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
|
||||
LOG.info("Reloading notebook repository for user {}", subject.getUser());
|
||||
notebookWsServer.broadcastReloadedNoteList(subject, null);
|
||||
return new JsonResponse<>(Status.OK, "", null).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific note repo.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ public class ZeppelinServer extends Application {
|
|||
new File(conf.getRelativeDir("zeppelin-web/src/app/spell")));
|
||||
}
|
||||
|
||||
this.helium = new Helium(
|
||||
ZeppelinServer.helium = new Helium(
|
||||
conf.getHeliumConfPath(),
|
||||
conf.getHeliumRegistry(),
|
||||
new File(conf.getRelativeDir(ConfVars.ZEPPELIN_DEP_LOCALREPO),
|
||||
|
|
@ -177,7 +177,7 @@ public class ZeppelinServer extends Application {
|
|||
// Web UI
|
||||
final WebAppContext webApp = setupWebAppContext(contexts, conf);
|
||||
|
||||
// REST api
|
||||
// Create `ZeppelinServer` using reflection and setup REST Api
|
||||
setupRestApiContextHandler(webApp, conf);
|
||||
|
||||
// Notebook server
|
||||
|
|
|
|||
|
|
@ -200,9 +200,9 @@ public class NotebookServer extends WebSocketServlet
|
|||
messagereceived.ticket, ticket);
|
||||
} else {
|
||||
if (!messagereceived.op.equals(OP.PING)) {
|
||||
conn.send(serializeMessage(new Message(OP.ERROR_INFO).put("info",
|
||||
conn.send(serializeMessage(new Message(OP.SESSION_LOGOUT).put("info",
|
||||
"Your ticket is invalid possibly due to server restart. "
|
||||
+ "Please refresh the page and login again.")));
|
||||
+ "Please login again.")));
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -717,7 +717,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
LOG.info("Cannot {}. Connection readers {}. Allowed readers {}", op, userAndRoles, allowed);
|
||||
|
||||
conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info",
|
||||
"Insufficient privileges to " + op + "note.\n\n" + "Allowed users or roles: " + allowed
|
||||
"Insufficient privileges to " + op + " note.\n\n" + "Allowed users or roles: " + allowed
|
||||
.toString() + "\n\n" + "But the user " + userName + " belongs to: " + userAndRoles
|
||||
.toString())));
|
||||
}
|
||||
|
|
@ -1184,15 +1184,21 @@ public class NotebookServer extends WebSocketServlet
|
|||
final Note note = notebook.getNote(noteId);
|
||||
Paragraph p = note.getParagraph(paragraphId);
|
||||
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
if (note.isPersonalizedMode()) {
|
||||
p = p.getUserParagraphMap().get(subject.getUser());
|
||||
}
|
||||
|
||||
p.settings.setParams(params);
|
||||
p.setConfig(config);
|
||||
p.setTitle((String) fromMessage.get("title"));
|
||||
p.setText((String) fromMessage.get("paragraph"));
|
||||
|
||||
AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal);
|
||||
if (note.isPersonalizedMode()) {
|
||||
p = p.getUserParagraph(subject.getUser());
|
||||
p.settings.setParams(params);
|
||||
p.setConfig(config);
|
||||
p.setTitle((String) fromMessage.get("title"));
|
||||
p.setText((String) fromMessage.get("paragraph"));
|
||||
}
|
||||
|
||||
|
||||
note.persist(subject);
|
||||
|
||||
if (note.isPersonalizedMode()) {
|
||||
|
|
@ -1759,6 +1765,15 @@ public class NotebookServer extends WebSocketServlet
|
|||
p.settings.setParams(params);
|
||||
p.setConfig(config);
|
||||
|
||||
if (note.isPersonalizedMode()) {
|
||||
p = note.getParagraph(paragraphId);
|
||||
p.setText(text);
|
||||
p.setTitle(title);
|
||||
p.setAuthenticationInfo(subject);
|
||||
p.settings.setParams(params);
|
||||
p.setConfig(config);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
@ -1877,7 +1892,15 @@ public class NotebookServer extends WebSocketServlet
|
|||
InterpreterResult.Type type, String output) {
|
||||
Message msg = new Message(OP.PARAGRAPH_UPDATE_OUTPUT).put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId).put("index", index).put("type", type).put("data", output);
|
||||
broadcast(noteId, msg);
|
||||
Note note = notebook().getNote(noteId);
|
||||
if (note.isPersonalizedMode()) {
|
||||
String user = note.getParagraph(paragraphId).getUser();
|
||||
if (null != user) {
|
||||
multicastToUser(user, msg);
|
||||
}
|
||||
} else {
|
||||
broadcast(noteId, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2146,7 +2169,9 @@ public class NotebookServer extends WebSocketServlet
|
|||
}
|
||||
}
|
||||
if (job instanceof Paragraph) {
|
||||
notebookServer.broadcastParagraph(note, (Paragraph) job);
|
||||
Paragraph p = (Paragraph) job;
|
||||
p.setStatusToUserParagraph(job.getStatus());
|
||||
notebookServer.broadcastParagraph(note, p);
|
||||
}
|
||||
try {
|
||||
notebookServer.broadcastUpdateNoteJobInfo(System.currentTimeMillis() - 5000);
|
||||
|
|
@ -2281,7 +2306,6 @@ public class NotebookServer extends WebSocketServlet
|
|||
resp.put("editor", notebook().getInterpreterSettingManager().
|
||||
getEditorSetting(interpreter, user, noteId, replName));
|
||||
conn.send(serializeMessage(resp));
|
||||
return;
|
||||
}
|
||||
|
||||
private void getInterpreterSettings(NotebookSocket conn, AuthenticationInfo subject)
|
||||
|
|
@ -2324,11 +2348,31 @@ public class NotebookServer extends WebSocketServlet
|
|||
.equals(WatcherSecurityKey.getKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send websocket message to all connections regardless of notebook id
|
||||
*/
|
||||
private void broadcastToAllConnections(String serialized) {
|
||||
broadcastToAllConnectionsExcept(null, serialized);
|
||||
}
|
||||
|
||||
private void broadcastToAllConnectionsExcept(NotebookSocket exclude, String serialized) {
|
||||
synchronized (connectedSockets) {
|
||||
for (NotebookSocket conn: connectedSockets) {
|
||||
if (exclude != null && exclude.equals(conn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
conn.send(serialized);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Cannot broadcast message to watcher", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastToWatchers(String noteId, String subject, Message message) {
|
||||
synchronized (watcherSockets) {
|
||||
if (watcherSockets.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (NotebookSocket watcher : watcherSockets) {
|
||||
try {
|
||||
watcher.send(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.util.regex.Pattern;
|
|||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.exec.DefaultExecutor;
|
||||
import org.apache.commons.exec.PumpStreamHandler;
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpMethodBase;
|
||||
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
||||
|
|
@ -84,7 +85,7 @@ public abstract class AbstractTestRestApi {
|
|||
"role1 = *\n" +
|
||||
"role2 = *\n" +
|
||||
"role3 = *\n" +
|
||||
"admin = *" +
|
||||
"admin = *\n" +
|
||||
"[urls]\n" +
|
||||
"/api/version = anon\n" +
|
||||
"/** = authc";
|
||||
|
|
@ -333,7 +334,7 @@ public abstract class AbstractTestRestApi {
|
|||
GetMethod request = null;
|
||||
boolean isRunning = true;
|
||||
try {
|
||||
request = httpGet("/");
|
||||
request = httpGet("/version");
|
||||
isRunning = request.getStatusCode() == 200;
|
||||
} catch (IOException e) {
|
||||
LOG.error("AbstractTestRestApi.checkIfServerIsRunning() fails .. ZeppelinServer is not running");
|
||||
|
|
@ -427,8 +428,14 @@ public abstract class AbstractTestRestApi {
|
|||
httpClient.executeMethod(postMethod);
|
||||
LOG.info("{} - {}", postMethod.getStatusCode(), postMethod.getStatusText());
|
||||
Pattern pattern = Pattern.compile("JSESSIONID=([a-zA-Z0-9-]*)");
|
||||
java.util.regex.Matcher matcher = pattern.matcher(postMethod.getResponseHeaders("Set-Cookie")[0].toString());
|
||||
return matcher.find()? matcher.group(1) : StringUtils.EMPTY;
|
||||
Header[] setCookieHeaders = postMethod.getResponseHeaders("Set-Cookie");
|
||||
for (Header setCookie : setCookieHeaders) {
|
||||
java.util.regex.Matcher matcher = pattern.matcher(setCookie.toString());
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
protected static boolean userAndPasswordAreNotBlank(String user, String pwd) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.zeppelin.rest;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.anyOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -80,6 +81,13 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
List<Map<String, Object>> listOfRepositories = getListOfReposotiry();
|
||||
assertThat(listOfRepositories.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test public void reloadRepositories() throws IOException {
|
||||
GetMethod get = httpGet("/notebook-repositories/reload");
|
||||
int status = get.getStatusCode();
|
||||
get.releaseConnection();
|
||||
assertThat(status, is(200));
|
||||
}
|
||||
|
||||
@Test public void setNewDirectoryForLocalDirectory() throws IOException {
|
||||
List<Map<String, Object>> listOfRepositories = getListOfReposotiry();
|
||||
|
|
@ -95,7 +103,7 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
if (StringUtils.isBlank(localVfs)) {
|
||||
// no loval VFS set...
|
||||
// no local VFS set...
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +119,7 @@ public class NotebookRepoRestApiTest extends AbstractTestRestApi {
|
|||
break;
|
||||
}
|
||||
}
|
||||
assertThat(updatedPath, is("/tmp/newDir"));
|
||||
assertThat(updatedPath, anyOf(is("/tmp/newDir"),is("/tmp/newDir/")));
|
||||
|
||||
// go back to normal
|
||||
payload = "{ \"name\": \"" + className + "\", \"settings\" : { \"Notebook Path\" : \"" + localVfs + "\" } }";
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();;
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
@ -50,21 +50,21 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
@Test
|
||||
public void testTicket() throws IOException {
|
||||
GetMethod get = httpGet("/security/ticket");
|
||||
GetMethod get = httpGet("/security/ticket", "admin", "password1");
|
||||
get.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
Map<String, String> body = (Map<String, String>) resp.get("body");
|
||||
collector.checkThat("Paramater principal", body.get("principal"),
|
||||
CoreMatchers.equalTo("anonymous"));
|
||||
CoreMatchers.equalTo("admin"));
|
||||
collector.checkThat("Paramater ticket", body.get("ticket"),
|
||||
CoreMatchers.equalTo("anonymous"));
|
||||
CoreMatchers.not("anonymous"));
|
||||
get.releaseConnection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserList() throws IOException {
|
||||
GetMethod get = httpGet("/security/userlist/admi");
|
||||
GetMethod get = httpGet("/security/userlist/admi", "admin", "password1");
|
||||
get.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
|
|
@ -75,7 +75,7 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
CoreMatchers.equalTo(true));
|
||||
get.releaseConnection();
|
||||
|
||||
GetMethod notUser = httpGet("/security/userlist/randomString");
|
||||
GetMethod notUser = httpGet("/security/userlist/randomString", "admin", "password1");
|
||||
notUser.addRequestHeader("Origin", "http://localhost");
|
||||
Map<String, Object> notUserResp = gson.fromJson(notUser.getResponseBodyAsString(),
|
||||
new TypeToken<Map<String, Object>>(){}.getType());
|
||||
|
|
@ -85,6 +85,5 @@ public class SecurityRestApiTest extends AbstractTestRestApi {
|
|||
|
||||
notUser.releaseConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class DirAccessTest extends AbstractTestRestApi {
|
|||
public void testDirAccessForbidden() throws Exception {
|
||||
synchronized (this) {
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_SERVER_DEFAULT_DIR_ALLOWED.getVarName(), "false");
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
AbstractTestRestApi.startUp();
|
||||
HttpClient httpClient = new HttpClient();
|
||||
GetMethod getMethod = new GetMethod(getUrlToTest() + "/app/");
|
||||
httpClient.executeMethod(getMethod);
|
||||
|
|
@ -43,7 +43,7 @@ public class DirAccessTest extends AbstractTestRestApi {
|
|||
public void testDirAccessOk() throws Exception {
|
||||
synchronized (this) {
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_SERVER_DEFAULT_DIR_ALLOWED.getVarName(), "true");
|
||||
AbstractTestRestApi.startUpWithAuthenticationEnable();
|
||||
AbstractTestRestApi.startUp();
|
||||
HttpClient httpClient = new HttpClient();
|
||||
GetMethod getMethod = new GetMethod(getUrlToTest() + "/app/");
|
||||
httpClient.executeMethod(getMethod);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"globals": {
|
||||
"angular": false,
|
||||
"inject": false,
|
||||
"_": false,
|
||||
"jQuery": false,
|
||||
"hljs": false,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Karma configuration
|
||||
// http://karma-runner.github.io/0.12/config/configuration-file.html
|
||||
// Generated on 2014-08-29 using
|
||||
// generator-karma 0.8.3
|
||||
|
||||
var webpackConfig = require('../webpack.config');
|
||||
var webpackConfig = require('./webpack.config');
|
||||
|
||||
module.exports = function(config) {
|
||||
'use strict';
|
||||
|
|
@ -13,7 +30,7 @@ module.exports = function(config) {
|
|||
autoWatch: true,
|
||||
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath: '../',
|
||||
basePath: './',
|
||||
|
||||
// testing framework to use (jasmine/mocha/qunit/...)
|
||||
frameworks: ['jasmine'],
|
||||
|
|
@ -75,7 +92,7 @@ module.exports = function(config) {
|
|||
|
||||
'src/index.js',
|
||||
// 'test/spec/**/*.js',
|
||||
{pattern: 'test/spec/**/*.js', watched: false},
|
||||
{pattern: 'src/**/*.test.js', watched: false},
|
||||
],
|
||||
|
||||
// list of files / patterns to exclude
|
||||
|
|
@ -108,12 +125,12 @@ module.exports = function(config) {
|
|||
preprocessors: {
|
||||
'src/*/{*.js,!(test)/**/*.js}': 'coverage',
|
||||
'src/index.js': ['webpack', 'sourcemap',],
|
||||
'test/spec/**/*.js': ['webpack', 'sourcemap',],
|
||||
'src/**/*.test.js': ['webpack', 'sourcemap',],
|
||||
},
|
||||
|
||||
coverageReporter: {
|
||||
type: 'html',
|
||||
dir: '../reports/zeppelin-web-coverage',
|
||||
dir: './reports/zeppelin-web-coverage',
|
||||
subdir: '.'
|
||||
},
|
||||
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
"dev": "npm-run-all --parallel dev:server dev:watch",
|
||||
"visdev": "npm-run-all --parallel visdev:server dev:watch",
|
||||
"pretest": "npm install karma-phantomjs-launcher",
|
||||
"test": "karma start test/karma.conf.js"
|
||||
"test": "karma start karma.conf.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"grunt-angular-templates": "^0.5.7",
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@
|
|||
<exclude>node/**</exclude>
|
||||
<exclude>node_modules/**</exclude>
|
||||
<exclude>bower_components/**</exclude>
|
||||
<exclude>test/**</exclude>
|
||||
<exclude>src/**/*.test.js</exclude>
|
||||
<exclude>reports/**</exclude>
|
||||
<exclude>dist/**</exclude>
|
||||
<exclude>src/.buildignore</exclude>
|
||||
<exclude>src/fonts/fontawesome*</exclude>
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
.when('/helium', {
|
||||
templateUrl: 'app/helium/helium.html',
|
||||
controller: 'HeliumCtrl'
|
||||
})
|
||||
})
|
||||
.when('/configuration', {
|
||||
templateUrl: 'app/configuration/configuration.html',
|
||||
controller: 'ConfigurationCtrl'
|
||||
|
|
@ -116,6 +116,24 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
timeout: 6000
|
||||
});
|
||||
})
|
||||
|
||||
//handel logout on API failure
|
||||
.config(function ($httpProvider, $provide) {
|
||||
$provide.factory('httpInterceptor', function ($q, $rootScope) {
|
||||
return {
|
||||
'responseError': function (rejection) {
|
||||
if (rejection.status === 405) {
|
||||
var data = {};
|
||||
data.info = '';
|
||||
$rootScope.$broadcast('session_logout', data);
|
||||
}
|
||||
$rootScope.$broadcast('httpResponseError', rejection);
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
});
|
||||
$httpProvider.interceptors.push('httpInterceptor');
|
||||
})
|
||||
.constant('TRASH_FOLDER_ID', '~Trash');
|
||||
|
||||
function auth() {
|
||||
|
|
|
|||
100
zeppelin-web/src/app/helium/helium.config.js
Normal file
100
zeppelin-web/src/app/helium/helium.config.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const HeliumConfFieldType = {
|
||||
NUMBER: 'number',
|
||||
JSON: 'json',
|
||||
STRING: 'string',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
|
||||
* @param spec <Object> including `value` for each conf key
|
||||
*/
|
||||
export function mergePersistedConfWithSpec(persisted, spec) {
|
||||
const confs = [];
|
||||
|
||||
for(let name in spec) {
|
||||
const specField = spec[name];
|
||||
const persistedValue = persisted[name];
|
||||
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue;
|
||||
const merged = {
|
||||
name: name, type: specField.type, description: specField.description,
|
||||
value: value, defaultValue: specField.defaultValue,
|
||||
};
|
||||
|
||||
confs.push(merged);
|
||||
}
|
||||
|
||||
return confs;
|
||||
}
|
||||
|
||||
export function createPackageConf(defaultPackages, persistedPackacgeConfs) {
|
||||
let packageConfs = {};
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgInfo = defaultPackages[name];
|
||||
|
||||
const configSpec = pkgInfo.pkg.config;
|
||||
if (!configSpec) { continue; }
|
||||
|
||||
const version = pkgInfo.pkg.version;
|
||||
if (!version) { continue; }
|
||||
|
||||
let config = {};
|
||||
if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) {
|
||||
config = persistedPackacgeConfs[name][version];
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(config, configSpec);
|
||||
packageConfs[name] = confs;
|
||||
}
|
||||
|
||||
return packageConfs;
|
||||
}
|
||||
|
||||
export function parseConfigValue(type, stringified) {
|
||||
let value = stringified;
|
||||
|
||||
try {
|
||||
if (HeliumConfFieldType.NUMBER === type) {
|
||||
value = parseFloat(stringified);
|
||||
} else if (HeliumConfFieldType.JSON === type) {
|
||||
value = JSON.parse(stringified);
|
||||
}
|
||||
} catch(error) {
|
||||
// return just the stringified one
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* create persistable config object
|
||||
*/
|
||||
export function createPersistableConfig(currentConf) {
|
||||
// persist key-value only
|
||||
// since other info (e.g type, desc) can be provided by default config
|
||||
const filtered = currentConf.reduce((acc, c) => {
|
||||
let value = parseConfigValue(c.type, c.value);
|
||||
acc[c.name] = value;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -12,16 +12,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { HeliumType, } from '../../components/helium/helium-type';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('HeliumCtrl', HeliumCtrl);
|
||||
|
||||
function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService) {
|
||||
export default function HeliumCtrl($scope, $rootScope, $sce,
|
||||
baseUrlSrv, ngToast, heliumService) {
|
||||
'ngInject';
|
||||
|
||||
$scope.packageInfos = {};
|
||||
$scope.defaultVersions = {};
|
||||
|
||||
$scope.pkgSearchResults = {};
|
||||
$scope.defaultPackages = {};
|
||||
$scope.showVersions = {};
|
||||
$scope.bundleOrder = [];
|
||||
$scope.bundleOrderChanged = false;
|
||||
|
|
@ -30,46 +28,42 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
$scope.intpTypePkg = {}
|
||||
$scope.appTypePkg = {}
|
||||
$scope.numberOfEachPackageByType = {}
|
||||
|
||||
$scope.allPackageTypes = [HeliumType][0]
|
||||
$scope.pkgListByType = 'VISUALIZATION'
|
||||
|
||||
$scope.defaultPackageConfigs = {}; // { pkgName, [{name, type, desc, value, defaultValue}] }
|
||||
$scope.intpDefaultIcon = $sce.trustAsHtml('<img src="../assets/images/maven_default_icon.png" style="width: 12px"/>');
|
||||
|
||||
var buildDefaultVersionListToDisplay = function(packageInfos) {
|
||||
var defaultVersions = {};
|
||||
// show enabled version if any version of package is enabled
|
||||
for (var name in packageInfos) {
|
||||
var pkgs = packageInfos[name];
|
||||
for (var pkgIdx in pkgs) {
|
||||
var pkg = pkgs[pkgIdx];
|
||||
pkg.pkg.icon = $sce.trustAsHtml(pkg.pkg.icon);
|
||||
if (pkg.enabled) {
|
||||
defaultVersions[name] = pkg;
|
||||
pkgs.splice(pkgIdx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// show first available version if package is not enabled
|
||||
if (!defaultVersions[name]) {
|
||||
defaultVersions[name] = pkgs[0];
|
||||
pkgs.splice(0, 1);
|
||||
}
|
||||
}
|
||||
$scope.defaultVersions = defaultVersions;
|
||||
};
|
||||
|
||||
function init() {
|
||||
// get all package info and set config
|
||||
heliumService.getAllPackageInfoAndDefaultPackages()
|
||||
.then(({ pkgSearchResults, defaultPackages }) => {
|
||||
$scope.pkgSearchResults = pkgSearchResults;
|
||||
$scope.defaultPackages = defaultPackages;
|
||||
classifyPkgType($scope.defaultPackages)
|
||||
return heliumService.getAllPackageConfigs()
|
||||
})
|
||||
.then(defaultPackageConfigs => {
|
||||
$scope.defaultPackageConfigs = defaultPackageConfigs;
|
||||
});
|
||||
|
||||
// 2. get vis package order
|
||||
heliumService.getVisualizationPackageOrder()
|
||||
.then(visPackageOrder => {
|
||||
$scope.bundleOrder = visPackageOrder;
|
||||
$scope.bundleOrderChanged = false;
|
||||
});
|
||||
}
|
||||
|
||||
var classifyPkgType = function(packageInfos) {
|
||||
var vizTypePkg = {}
|
||||
var spellTypePkg = {}
|
||||
var intpTypePkg = {}
|
||||
var appTypePkg = {}
|
||||
|
||||
|
||||
for (var name in packageInfos) {
|
||||
var pkgs = packageInfos[name]
|
||||
var pkgType = pkgs.pkg.type
|
||||
|
||||
|
||||
switch (pkgType) {
|
||||
case HeliumType.VISUALIZATION:
|
||||
vizTypePkg[name] = pkgs;
|
||||
|
|
@ -92,29 +86,7 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
$scope.appTypePkg = appTypePkg
|
||||
$scope.intpTypePkg = intpTypePkg
|
||||
};
|
||||
|
||||
var getAllPackageInfo = function() {
|
||||
heliumService.getAllPackageInfo().
|
||||
success(function(data, status) {
|
||||
$scope.packageInfos = data.body;
|
||||
buildDefaultVersionListToDisplay($scope.packageInfos);
|
||||
classifyPkgType($scope.defaultVersions)
|
||||
}).
|
||||
error(function(data, status) {
|
||||
console.log('Can not load package info %o %o', status, data);
|
||||
});
|
||||
};
|
||||
|
||||
var getBundleOrder = function() {
|
||||
heliumService.getVisualizationPackageOrder().
|
||||
success(function(data, status) {
|
||||
$scope.bundleOrder = data.body;
|
||||
}).
|
||||
error(function(data, status) {
|
||||
console.log('Can not get bundle order %o %o', status, data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.bundleOrderListeners = {
|
||||
accept: function(sourceItemHandleScope, destSortableScope) {return true;},
|
||||
itemMoved: function(event) {},
|
||||
|
|
@ -122,15 +94,7 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
$scope.bundleOrderChanged = true;
|
||||
}
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
getAllPackageInfo();
|
||||
getBundleOrder();
|
||||
$scope.bundleOrderChanged = false;
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
|
||||
$scope.saveBundleOrder = function() {
|
||||
var confirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
|
|
@ -160,35 +124,35 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var getLicense = function(name, artifact) {
|
||||
var pkg = _.filter($scope.defaultVersions[name], function(p) {
|
||||
var filteredPkgSearchResults = _.filter($scope.defaultPackages[name], function(p) {
|
||||
return p.artifact === artifact;
|
||||
});
|
||||
|
||||
|
||||
var license;
|
||||
if (pkg.length === 0) {
|
||||
pkg = _.filter($scope.packageInfos[name], function(p) {
|
||||
if (filteredPkgSearchResults.length === 0) {
|
||||
filteredPkgSearchResults = _.filter($scope.pkgSearchResults[name], function(p) {
|
||||
return p.pkg.artifact === artifact;
|
||||
});
|
||||
|
||||
if (pkg.length > 0) {
|
||||
license = pkg[0].pkg.license;
|
||||
|
||||
if (filteredPkgSearchResults.length > 0) {
|
||||
license = filteredPkgSearchResults[0].pkg.license;
|
||||
}
|
||||
} else {
|
||||
license = pkg[0].license;
|
||||
license = filteredPkgSearchResults[0].license;
|
||||
}
|
||||
|
||||
|
||||
if (!license) {
|
||||
license = 'Unknown';
|
||||
}
|
||||
return license;
|
||||
}
|
||||
|
||||
|
||||
$scope.enable = function(name, artifact) {
|
||||
var license = getLicense(name, artifact);
|
||||
|
||||
|
||||
var confirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
closeByBackdrop: false,
|
||||
|
|
@ -221,7 +185,7 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.disable = function(name) {
|
||||
var confirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
|
|
@ -252,7 +216,7 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.toggleVersions = function(pkgName) {
|
||||
if ($scope.showVersions[pkgName]) {
|
||||
$scope.showVersions[pkgName] = false;
|
||||
|
|
@ -260,12 +224,12 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
$scope.showVersions[pkgName] = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.isLocalPackage = function(pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
return pkg.artifact && !pkg.artifact.includes('@');
|
||||
};
|
||||
|
||||
|
||||
$scope.hasNpmLink = function(pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
return (pkg.type === HeliumType.SPELL || pkg.type === HeliumType.VISUALIZATION) &&
|
||||
|
|
@ -285,4 +249,48 @@ function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService
|
|||
})
|
||||
return _.compact(result).length
|
||||
}
|
||||
|
||||
$scope.configExists = function(pkgSearchResult) {
|
||||
// helium package config is persisted per version
|
||||
return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact;
|
||||
};
|
||||
|
||||
$scope.configOpened = function(pkgSearchResult) {
|
||||
return pkgSearchResult.configOpened && !pkgSearchResult.configFetching;
|
||||
};
|
||||
|
||||
$scope.getConfigButtonClass = function(pkgSearchResult) {
|
||||
return (pkgSearchResult.configOpened && pkgSearchResult.configFetching) ?
|
||||
'disabled' : '';
|
||||
}
|
||||
|
||||
$scope.toggleConfigButton = function(pkgSearchResult) {
|
||||
if (pkgSearchResult.configOpened) {
|
||||
pkgSearchResult.configOpened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
const pkgName = pkg.name;
|
||||
pkgSearchResult.configFetching = true;
|
||||
pkgSearchResult.configOpened = true;
|
||||
|
||||
heliumService.getSinglePackageConfigs(pkg)
|
||||
.then(confs => {
|
||||
$scope.defaultPackageConfigs[pkgName] = confs;
|
||||
pkgSearchResult.configFetching = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveConfig = function(pkgSearchResult) {
|
||||
const pkgName = pkgSearchResult.pkg.name;
|
||||
const currentConf = $scope.defaultPackageConfigs[pkgName];
|
||||
|
||||
heliumService.saveConfig(pkgSearchResult.pkg, currentConf, () => {
|
||||
// close after config is saved
|
||||
pkgSearchResult.configOpened = false;
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@
|
|||
width: 500px;
|
||||
}
|
||||
|
||||
.spellConfigButton {
|
||||
background-color: #FEFEFE;
|
||||
}
|
||||
|
||||
.heliumPackageList .heliumPackageDisabledArtifact {
|
||||
color:gray;
|
||||
}
|
||||
|
|
@ -158,3 +162,18 @@
|
|||
color: #aaaaaa;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.heliumConfig {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.heliumConfigTable {
|
||||
margin-top: 15px;
|
||||
vertical-align:middle;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.heliumConfigValueText {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ limitations under the License.
|
|||
<div class="btn-group" data-ng-repeat="pkgName in bundleOrder"
|
||||
as-sortable-item>
|
||||
<div class="btn btn-default btn-sm"
|
||||
ng-bind-html='defaultVersions[pkgName].pkg.icon'
|
||||
ng-bind-html='defaultPackages[pkgName].pkg.icon'
|
||||
as-sortable-item-handle>
|
||||
</div>
|
||||
</div>
|
||||
<span class="saveLink"
|
||||
ng-show="bundleOrderChanged"
|
||||
ng-click="saveBundleOrder()">
|
||||
ng-show="bundleOrderChanged"
|
||||
ng-click="saveBundleOrder()">
|
||||
save
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -63,72 +63,72 @@ limitations under the License.
|
|||
<div class="box width-full heliumPackageContainer">
|
||||
<div class="row"
|
||||
style="padding-bottom: 15px"
|
||||
ng-if="getPackageSize(defaultVersions, pkgListByType) === 0">
|
||||
ng-if="getPackageSize(defaultPackages, pkgListByType) === 0">
|
||||
<div class="col-md-12 gray40-message">
|
||||
<em>Currently there is no available package to be listed</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row heliumPackageList"
|
||||
ng-repeat="(pkgName, pkgInfo) in defaultVersions"
|
||||
ng-show="$parent.pkgListByType == pkgInfo.pkg.type">
|
||||
ng-repeat="(pkgName, pkgSearchResult) in defaultPackages"
|
||||
ng-show="$parent.pkgListByType == pkgSearchResult.pkg.type">
|
||||
<div class="col-md-12">
|
||||
<div class="heliumPackageHead">
|
||||
<div class="heliumPackageIcon"
|
||||
ng-if="pkgInfo.pkg.type !== 'INTERPRETER'"
|
||||
ng-bind-html=pkgInfo.pkg.icon></div>
|
||||
ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
|
||||
ng-bind-html=pkgSearchResult.pkg.icon></div>
|
||||
<div class="heliumPackageIcon"
|
||||
ng-if="pkgInfo.pkg.type === 'INTERPRETER'"
|
||||
ng-if="pkgSearchResult.pkg.type === 'INTERPRETER'"
|
||||
ng-bind-html=intpDefaultIcon></div>
|
||||
<div class="heliumPackageName">
|
||||
<span ng-if="hasNpmLink(pkgInfo)">
|
||||
<span ng-if="hasNpmLink(pkgSearchResult)">
|
||||
<a target="_blank" href="https://www.npmjs.com/package/{{pkgName}}">{{pkgName}}</a>
|
||||
</span>
|
||||
<span ng-if="hasMavenLink(pkgInfo)">
|
||||
<span ng-if="hasMavenLink(pkgSearchResult)">
|
||||
<a target="_blank"
|
||||
href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22{{pkgInfo.pkg.artifact.split('@')[0]}}%22%20AND%20v%3A%22{{pkgInfo.pkg.artifact.split('@')[1]}}%22">
|
||||
href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22{{pkgSearchResult.pkg.artifact.split('@')[0]}}%22%20AND%20v%3A%22{{pkgSearchResult.pkg.artifact.split('@')[1]}}%22">
|
||||
{{pkgName}}
|
||||
</a>
|
||||
</span>
|
||||
<span ng-if="!hasNpmLink(pkgInfo) && !hasMavenLink(pkgInfo)" ng-class="{'heliumLocalPackage': isLocalPackage(pkgInfo)}">
|
||||
<span ng-if="!hasNpmLink(pkgSearchResult) && !hasMavenLink(pkgSearchResult)" ng-class="{'heliumLocalPackage': isLocalPackage(pkgSearchResult)}">
|
||||
{{pkgName}}
|
||||
</span>
|
||||
<span class="heliumType">{{pkgInfo.pkg.type}}</span>
|
||||
<span class="heliumType">{{pkgSearchResult.pkg.type}}</span>
|
||||
</div>
|
||||
<div ng-show="!pkgInfo.enabled"
|
||||
ng-click="enable(pkgName, pkgInfo.pkg.artifact)"
|
||||
ng-if="pkgInfo.pkg.type !== 'INTERPRETER'"
|
||||
<div ng-show="!pkgSearchResult.enabled"
|
||||
ng-click="enable(pkgName, pkgSearchResult.pkg.artifact)"
|
||||
ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
|
||||
class="btn btn-success btn-xs"
|
||||
style="float:right">Enable</div>
|
||||
<div ng-show="pkgInfo.enabled"
|
||||
<div ng-show="pkgSearchResult.enabled"
|
||||
ng-click="disable(pkgName)"
|
||||
ng-if="pkgInfo.pkg.type !== 'INTERPRETER'"
|
||||
ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
|
||||
class="btn btn-info btn-xs"
|
||||
style="float:right">Disable</div>
|
||||
<div ng-show="!pkgInfo.enabled"
|
||||
ng-click="enable(pkgName, pkgInfo.pkg.artifact)"
|
||||
ng-if="pkgInfo.pkg.type === 'INTERPRETER'"
|
||||
<div ng-show="!pkgSearchResult.enabled"
|
||||
ng-click="enable(pkgName, pkgSearchResult.pkg.artifact)"
|
||||
ng-if="pkgSearchResult.pkg.type === 'INTERPRETER'"
|
||||
class="btn btn-success disabled btn-xs"
|
||||
style="float:right">Coming soon</div>
|
||||
</div>
|
||||
<div ng-class="{heliumPackageDisabledArtifact: !pkgInfo.enabled, heliumPackageEnabledArtifact: pkgInfo.enabled}">
|
||||
{{pkgInfo.pkg.artifact}}
|
||||
<div ng-class="{heliumPackageDisabledArtifact: !pkgSearchResult.enabled, heliumPackageEnabledArtifact: pkgSearchResult.enabled}">
|
||||
{{pkgSearchResult.pkg.artifact}}
|
||||
<span ng-show="packageInfos[pkgName].length > 0"
|
||||
ng-click="toggleVersions(pkgName)">
|
||||
versions
|
||||
</span>
|
||||
</div>
|
||||
<ul class="heliumPackageVersions"
|
||||
ng-show="showVersions[pkgName]">
|
||||
ng-show="showVersions[pkgName]">
|
||||
<li class="heliumPackageDisabledArtifact"
|
||||
ng-repeat="pkg in packageInfos[pkgName]">
|
||||
ng-repeat="pkg in packageInfos[pkgName]">
|
||||
{{pkg.pkg.artifact}} -
|
||||
<span ng-click="enable(pkgName, pkg.pkg.artifact)"
|
||||
ng-if="pkgInfo.pkg.type !== 'INTERPRETER'"
|
||||
ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
|
||||
style="margin-left:3px;cursor:pointer;text-decoration: underline;color:#3071a9">
|
||||
enable
|
||||
</span>
|
||||
<a target="_blank"
|
||||
ng-if="pkgInfo.pkg.type === 'INTERPRETER'"
|
||||
ng-if="pkgSearchResult.pkg.type === 'INTERPRETER'"
|
||||
style="margin-left:3px;cursor:pointer;text-decoration: underline;color:#3071a9"
|
||||
href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22{{pkg.pkg.artifact.split('@')[0]}}%22%20AND%20v%3A%22{{pkg.pkg.artifact.split('@')[1]}}%22">
|
||||
see more
|
||||
|
|
@ -136,17 +136,17 @@ limitations under the License.
|
|||
</li>
|
||||
</ul>
|
||||
<div class="heliumPackageDescription">
|
||||
{{pkgInfo.pkg.description}}
|
||||
{{pkgSearchResult.pkg.description}}
|
||||
</div>
|
||||
<div ng-if="pkgInfo.pkg.type === 'SPELL' && pkgInfo.pkg.spell"
|
||||
<div ng-if="pkgSearchResult.pkg.type === 'SPELL' && pkgSearchResult.pkg.spell"
|
||||
class="spellInfo">
|
||||
<div>
|
||||
<span class="spellInfoDesc">MAGIC</span>
|
||||
<span class="spellInfoValue">{{pkgInfo.pkg.spell.magic}} </span>
|
||||
<span class="spellInfoValue">{{pkgSearchResult.pkg.spell.magic}} </span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="spellInfoDesc">USAGE</span>
|
||||
<pre class="spellUsage">{{pkgInfo.pkg.spell.usage}} </pre>
|
||||
<pre class="spellUsage">{{pkgSearchResult.pkg.spell.usage}} </pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
19
zeppelin-web/src/app/helium/index.js
Normal file
19
zeppelin-web/src/app/helium/index.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import HeliumController from './helium.controller';
|
||||
|
||||
angular.module('zeppelinWebApp')
|
||||
.controller('HeliumCtrl', HeliumController);
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ body {
|
|||
}
|
||||
|
||||
.bodyAsIframe {
|
||||
padding-top: 20px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ limitations under the License.
|
|||
ng-controller="ParagraphCtrl"
|
||||
ng-init="init(currentParagraph, home.note)"
|
||||
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
|
||||
class="paragraph-col">
|
||||
style="margin: 0; padding: 0;">
|
||||
<div id="{{currentParagraph.id}}_paragraphColumn"
|
||||
ng-if="currentParagraph.results"
|
||||
ng-include src="'app/notebook/paragraph/paragraph.html'"
|
||||
|
|
|
|||
|
|
@ -709,6 +709,7 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
|
|||
$scope.permissions.owners = angular.element('#selectOwners').val();
|
||||
$scope.permissions.readers = angular.element('#selectReaders').val();
|
||||
$scope.permissions.writers = angular.element('#selectWriters').val();
|
||||
angular.element('.permissionsForm select').find('option:not([is-select2="false"])').remove();
|
||||
}
|
||||
|
||||
$scope.restartInterpreter = function(interpeter) {
|
||||
|
|
|
|||
|
|
@ -79,19 +79,19 @@ limitations under the License.
|
|||
data-ng-model="permissions">
|
||||
<p><span class="owners">Owners </span>
|
||||
<select id="selectOwners" multiple="multiple">
|
||||
<option ng-repeat="owner in permissions.owners" selected="selected">{{owner}}</option>
|
||||
<option is-select2="false" ng-repeat="owner in permissions.owners" selected="selected">{{owner}}</option>
|
||||
</select>
|
||||
Owners can change permissions,read and write the note.
|
||||
</p>
|
||||
<p><span class="writers">Writers </span>
|
||||
<select id="selectWriters" multiple="multiple">
|
||||
<option ng-repeat="writers in permissions.writers" selected="selected">{{writers}}</option>
|
||||
<option is-select2="false" ng-repeat="writers in permissions.writers" selected="selected">{{writers}}</option>
|
||||
</select>
|
||||
Writers can read and write the note.
|
||||
</p>
|
||||
<p><span class="readers">Readers </span>
|
||||
<select id="selectReaders" multiple="multiple">
|
||||
<option ng-repeat="readers in permissions.readers" selected="selected">{{readers}}</option>
|
||||
<option is-select2="false" ng-repeat="readers in permissions.readers" selected="selected">{{readers}}</option>
|
||||
</select>
|
||||
Readers can only read the note.
|
||||
</p>
|
||||
|
|
@ -112,7 +112,7 @@ limitations under the License.
|
|||
ng-controller="ParagraphCtrl"
|
||||
ng-init="init(currentParagraph, note)"
|
||||
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
|
||||
class="paragraph-col">
|
||||
style="margin: 0; padding: 0;">
|
||||
<div class="new-paragraph" ng-click="insertNew('above')" ng-hide="viewOnly || asIframe || revisionView">
|
||||
<h4 class="plus-sign">+</h4>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.originalText = '';
|
||||
$scope.editor = null;
|
||||
|
||||
// transactional info for spell execution
|
||||
$scope.spellTransaction = {
|
||||
totalResultCount: 0, renderedResultCount: 0,
|
||||
propagated: false, resultsMsg: [], paragraphText: '',
|
||||
};
|
||||
|
||||
var editorSetting = {};
|
||||
// flag that is used to set editor setting on paste percent sign
|
||||
var pastePercentSign = false;
|
||||
|
|
@ -227,7 +233,6 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.paragraph.status = 'ERROR';
|
||||
$scope.paragraph.errorMessage = errorMessage;
|
||||
console.error('Failed to execute interpret() in spell\n', error);
|
||||
if (digestRequired) { $scope.$digest(); }
|
||||
|
||||
if (!propagated) {
|
||||
$scope.propagateSpellResult(
|
||||
|
|
@ -237,9 +242,49 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
}
|
||||
};
|
||||
|
||||
$scope.runParagraphUsingSpell = function(spell, paragraphText,
|
||||
$scope.prepareSpellTransaction = function(resultsMsg, propagated, paragraphText) {
|
||||
$scope.spellTransaction.totalResultCount = resultsMsg.length;
|
||||
$scope.spellTransaction.renderedResultCount = 0;
|
||||
$scope.spellTransaction.propagated = propagated;
|
||||
$scope.spellTransaction.resultsMsg = resultsMsg;
|
||||
$scope.spellTransaction.paragraphText = paragraphText;
|
||||
};
|
||||
|
||||
/**
|
||||
* - update spell transaction count and
|
||||
* - check transaction is finished based on the result count
|
||||
* @returns {boolean}
|
||||
*/
|
||||
$scope.increaseSpellTransactionResultCount = function() {
|
||||
$scope.spellTransaction.renderedResultCount += 1;
|
||||
|
||||
const total = $scope.spellTransaction.totalResultCount;
|
||||
const current = $scope.spellTransaction.renderedResultCount;
|
||||
return total === current;
|
||||
};
|
||||
|
||||
$scope.cleanupSpellTransaction = function() {
|
||||
const status = 'FINISHED';
|
||||
$scope.paragraph.status = status;
|
||||
$scope.paragraph.results.code = status;
|
||||
|
||||
const propagated = $scope.spellTransaction.propagated;
|
||||
const resultsMsg = $scope.spellTransaction.resultsMsg;
|
||||
const paragraphText = $scope.spellTransaction.paragraphText;
|
||||
|
||||
if (!propagated) {
|
||||
const propagable = SpellResult.createPropagable(resultsMsg);
|
||||
$scope.propagateSpellResult(
|
||||
$scope.paragraph.id, $scope.paragraph.title,
|
||||
paragraphText, propagable, status, '',
|
||||
$scope.paragraph.config, $scope.paragraph.settings.params);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.runParagraphUsingSpell = function(paragraphText,
|
||||
magic, digestRequired, propagated) {
|
||||
$scope.paragraph.results = {};
|
||||
$scope.paragraph.status = 'PENDING';
|
||||
$scope.paragraph.errorMessage = '';
|
||||
if (digestRequired) { $scope.$digest(); }
|
||||
|
||||
|
|
@ -248,30 +293,20 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
const splited = paragraphText.split(magic);
|
||||
// remove leading spaces
|
||||
const textWithoutMagic = splited[1].replace(/^\s+/g, '');
|
||||
const spellResult = spell.interpret(textWithoutMagic);
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(
|
||||
heliumService.getAllSpells(), magic, textWithoutMagic);
|
||||
|
||||
// handle actual result message in promise
|
||||
parsed.then(resultsMsg => {
|
||||
const status = 'FINISHED';
|
||||
$scope.paragraph.status = status;
|
||||
$scope.paragraph.results.code = status;
|
||||
$scope.paragraph.results.msg = resultsMsg;
|
||||
$scope.paragraph.config.tableHide = false;
|
||||
if (digestRequired) { $scope.$digest(); }
|
||||
heliumService.executeSpell(magic, textWithoutMagic)
|
||||
.then(resultsMsg => {
|
||||
$scope.prepareSpellTransaction(resultsMsg, propagated, paragraphText);
|
||||
|
||||
if (!propagated) {
|
||||
const propagable = SpellResult.createPropagable(resultsMsg);
|
||||
$scope.propagateSpellResult(
|
||||
$scope.paragraph.id, $scope.paragraph.title,
|
||||
paragraphText, propagable, status, '',
|
||||
$scope.paragraph.config, $scope.paragraph.settings.params);
|
||||
}
|
||||
}).catch(error => {
|
||||
$scope.handleSpellError(paragraphText, error,
|
||||
digestRequired, propagated);
|
||||
});
|
||||
$scope.paragraph.results.msg = resultsMsg;
|
||||
$scope.paragraph.config.tableHide = false;
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
$scope.handleSpellError(paragraphText, error,
|
||||
digestRequired, propagated);
|
||||
});
|
||||
} catch (error) {
|
||||
$scope.handleSpellError(paragraphText, error,
|
||||
digestRequired, propagated);
|
||||
|
|
@ -309,11 +344,9 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
}
|
||||
|
||||
const magic = SpellResult.extractMagic(paragraphText);
|
||||
const spell = heliumService.getSpellByMagic(magic);
|
||||
|
||||
if (spell) {
|
||||
$scope.runParagraphUsingSpell(
|
||||
spell, paragraphText, magic, digestRequired, propagated);
|
||||
if (heliumService.getSpellByMagic(magic)) {
|
||||
$scope.runParagraphUsingSpell(paragraphText, magic, digestRequired, propagated);
|
||||
} else {
|
||||
$scope.runParagraphUsingBackendInterpreter(paragraphText);
|
||||
}
|
||||
|
|
@ -509,10 +542,8 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
|
||||
$scope.changeColWidth = function(paragraph, width) {
|
||||
angular.element('.navbar-right.open').removeClass('open');
|
||||
if (width !== paragraph.config.colWidth) {
|
||||
paragraph.config.colWidth = width;
|
||||
commitParagraph(paragraph);
|
||||
}
|
||||
paragraph.config.colWidth = width;
|
||||
commitParagraph(paragraph);
|
||||
};
|
||||
|
||||
$scope.toggleOutput = function(paragraph) {
|
||||
|
|
@ -1130,23 +1161,23 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.paragraph.config = newPara.config;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.updateParagraph = function(oldPara, newPara, updateCallback) {
|
||||
// 1. get status, refreshed
|
||||
const statusChanged = (newPara.status !== oldPara.status);
|
||||
const resultRefreshed = (newPara.dateFinished !== oldPara.dateFinished) ||
|
||||
isEmpty(newPara.results) !== isEmpty(oldPara.results) ||
|
||||
newPara.status === 'ERROR' || (newPara.status === 'FINISHED' && statusChanged);
|
||||
|
||||
|
||||
// 2. update texts managed by $scope
|
||||
$scope.updateAllScopeTexts(oldPara, newPara);
|
||||
|
||||
|
||||
// 3. execute callback to update result
|
||||
updateCallback();
|
||||
|
||||
|
||||
// 4. update remaining paragraph objects
|
||||
$scope.updateParagraphObjectWhenUpdated(newPara);
|
||||
|
||||
|
||||
// 5. handle scroll down by key properly if new paragraph is added
|
||||
if (statusChanged || resultRefreshed) {
|
||||
// when last paragraph runs, zeppelin automatically appends new paragraph.
|
||||
|
|
@ -1159,6 +1190,8 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
}
|
||||
};
|
||||
|
||||
/** $scope.$on */
|
||||
|
||||
$scope.$on('runParagraphUsingSpell', function(event, data) {
|
||||
const oldPara = $scope.paragraph;
|
||||
let newPara = data.paragraph;
|
||||
|
|
@ -1343,4 +1376,17 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.$on('closeTable', function(event) {
|
||||
$scope.closeTable($scope.paragraph);
|
||||
});
|
||||
|
||||
$scope.$on('resultRendered', function(event, paragraphId) {
|
||||
if ($scope.paragraph.id !== paragraphId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** increase spell result count and return if not finished */
|
||||
if (!$scope.increaseSpellTransactionResultCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.cleanupSpellTransaction();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,7 +267,9 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
|
||||
$scope.renderDefaultDisplay = function(targetElemId, type, data, refresh) {
|
||||
if (type === DefaultDisplayType.TABLE) {
|
||||
$scope.renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
$timeout(function() {
|
||||
$scope.renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
}, 10);
|
||||
} else if (type === DefaultDisplayType.HTML) {
|
||||
renderHtml(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ANGULAR) {
|
||||
|
|
@ -279,6 +281,10 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
} else {
|
||||
console.error(`Unknown Display Type: ${type}`);
|
||||
}
|
||||
|
||||
// send message to parent that this result is rendered
|
||||
const paragraphId = $scope.$parent.paragraph.id;
|
||||
$scope.$emit('resultRendered', paragraphId);
|
||||
};
|
||||
|
||||
const renderResult = function(type, refresh) {
|
||||
|
|
@ -294,12 +300,7 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
renderApp(`p${appState.id}`, appState);
|
||||
} else {
|
||||
if (!DefaultDisplayType[type]) {
|
||||
const spell = heliumService.getSpellByMagic(type);
|
||||
if (!spell) {
|
||||
console.error(`Can't execute spell due to unknown display type: ${type}`);
|
||||
return;
|
||||
}
|
||||
$scope.renderCustomDisplay(type, data, spell);
|
||||
$scope.renderCustomDisplay(type, data);
|
||||
} else {
|
||||
const targetElemId = $scope.createDisplayDOMId(`p${$scope.id}`, type);
|
||||
$scope.renderDefaultDisplay(targetElemId, type, data, refresh);
|
||||
|
|
@ -314,38 +315,40 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
/**
|
||||
* Render multiple sub results for custom display
|
||||
*/
|
||||
$scope.renderCustomDisplay = function(type, data, spell) {
|
||||
$scope.renderCustomDisplay = function(type, data) {
|
||||
// get result from intp
|
||||
|
||||
const spellResult = spell.interpret(data.trim());
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(
|
||||
heliumService.getAllSpells());
|
||||
if (!heliumService.getSpellByMagic(type)) {
|
||||
console.error(`Can't execute spell due to unknown display type: ${type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// custom display result can include multiple subset results
|
||||
parsed.then(dataWithTypes => {
|
||||
const containerDOMId = `p${$scope.id}_custom`;
|
||||
const afterLoaded = () => {
|
||||
const containerDOM = angular.element(`#${containerDOMId}`);
|
||||
// Spell.interpret() can create multiple outputs
|
||||
for(let i = 0; i < dataWithTypes.length; i++) {
|
||||
const dt = dataWithTypes[i];
|
||||
const data = dt.data;
|
||||
const type = dt.type;
|
||||
heliumService.executeSpellAsDisplaySystem(type, data)
|
||||
.then(dataWithTypes => {
|
||||
const containerDOMId = `p${$scope.id}_custom`;
|
||||
const afterLoaded = () => {
|
||||
const containerDOM = angular.element(`#${containerDOMId}`);
|
||||
// Spell.interpret() can create multiple outputs
|
||||
for(let i = 0; i < dataWithTypes.length; i++) {
|
||||
const dt = dataWithTypes[i];
|
||||
const data = dt.data;
|
||||
const type = dt.type;
|
||||
|
||||
// prepare each DOM to be filled
|
||||
const subResultDOMId = $scope.createDisplayDOMId(`p${$scope.id}_custom_${i}`, type);
|
||||
const subResultDOM = document.createElement('div');
|
||||
containerDOM.append(subResultDOM);
|
||||
subResultDOM.setAttribute('id', subResultDOMId);
|
||||
// prepare each DOM to be filled
|
||||
const subResultDOMId = $scope.createDisplayDOMId(`p${$scope.id}_custom_${i}`, type);
|
||||
const subResultDOM = document.createElement('div');
|
||||
containerDOM.append(subResultDOM);
|
||||
subResultDOM.setAttribute('id', subResultDOMId);
|
||||
|
||||
$scope.renderDefaultDisplay(subResultDOMId, type, data, true);
|
||||
}
|
||||
};
|
||||
$scope.renderDefaultDisplay(subResultDOMId, type, data, true);
|
||||
}
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(containerDOMId, afterLoaded);
|
||||
}).catch(error => {
|
||||
console.error(`Failed to render custom display: ${$scope.type}\n` + error);
|
||||
});
|
||||
retryUntilElemIsLoaded(containerDOMId, afterLoaded);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Failed to render custom display: ${$scope.type}\n` + error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,9 +31,10 @@ export class SpellBase {
|
|||
* Consumes text and return `SpellResult`.
|
||||
*
|
||||
* @param paragraphText {string} which doesn't include magic
|
||||
* @param config {Object}
|
||||
* @return {SpellResult}
|
||||
*/
|
||||
interpret(paragraphText) {
|
||||
interpret(paragraphText, config) {
|
||||
throw new Error('SpellBase.interpret() should be overrided');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import TableData from '../../../src/app/tabledata/tabledata.js';
|
||||
import TableData from './tabledata.js';
|
||||
|
||||
describe('TableData build', function() {
|
||||
var td;
|
||||
|
|
@ -100,7 +100,7 @@ export default class LinechartVisualization extends Nvd3ChartVisualization {
|
|||
<input type="checkbox"
|
||||
ng-model="config.lineWithFocus"
|
||||
ng-click="save()" />
|
||||
show line chart with focus
|
||||
zoom
|
||||
</label>
|
||||
</div>`,
|
||||
scope: {
|
||||
|
|
|
|||
96
zeppelin-web/src/components/helium/helium-conf.js
Normal file
96
zeppelin-web/src/components/helium/helium-conf.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const HeliumConfFieldType = {
|
||||
NUMBER: 'number',
|
||||
JSON: 'json',
|
||||
STRING: 'string',
|
||||
};
|
||||
|
||||
/**
|
||||
* @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
|
||||
* @param spec <Object> including `value` for each conf key
|
||||
*/
|
||||
export function mergePersistedConfWithSpec(persisted, spec) {
|
||||
const confs = [];
|
||||
|
||||
for(let name in spec) {
|
||||
const specField = spec[name];
|
||||
const persistedValue = persisted[name];
|
||||
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue;
|
||||
const merged = {
|
||||
name: name, type: specField.type, description: specField.description,
|
||||
value: value, defaultValue: specField.defaultValue,
|
||||
};
|
||||
|
||||
confs.push(merged);
|
||||
}
|
||||
|
||||
return confs;
|
||||
}
|
||||
|
||||
export function createAllPackageConfigs(defaultPackages, persistedConfs) {
|
||||
let packageConfs = {};
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgSearchResult = defaultPackages[name];
|
||||
|
||||
const spec = pkgSearchResult.pkg.config;
|
||||
if (!spec) { continue; }
|
||||
|
||||
const artifact = pkgSearchResult.pkg.artifact;
|
||||
if (!artifact) { continue; }
|
||||
|
||||
let persistedConf = {};
|
||||
if (persistedConfs[artifact]) {
|
||||
persistedConf = persistedConfs[artifact];
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(persistedConf, spec);
|
||||
packageConfs[name] = confs;
|
||||
}
|
||||
|
||||
return packageConfs;
|
||||
}
|
||||
|
||||
export function parseConfigValue(type, stringified) {
|
||||
let value = stringified;
|
||||
|
||||
try {
|
||||
if (HeliumConfFieldType.NUMBER === type) {
|
||||
value = parseFloat(stringified);
|
||||
} else if (HeliumConfFieldType.JSON === type) {
|
||||
value = JSON.parse(stringified);
|
||||
}
|
||||
} catch(error) {
|
||||
// return just the stringified one
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* persist key-value only
|
||||
* since other info (e.g type, desc) can be provided by default config
|
||||
*/
|
||||
export function createPersistableConfig(currentConfs) {
|
||||
const filtered = currentConfs.reduce((acc, c) => {
|
||||
acc[c.name] = parseConfigValue(c.type, c.value);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
47
zeppelin-web/src/components/helium/helium-package.js
Normal file
47
zeppelin-web/src/components/helium/helium-package.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export function createDefaultPackage(pkgSearchResult, sce) {
|
||||
for (let pkgIdx in pkgSearchResult) {
|
||||
const pkg = pkgSearchResult[pkgIdx];
|
||||
pkg.pkg.icon = sce.trustAsHtml(pkg.pkg.icon);
|
||||
if (pkg.enabled) {
|
||||
pkgSearchResult.splice(pkgIdx, 1);
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
|
||||
// show first available version if package is not enabled
|
||||
const result = pkgSearchResult[0];
|
||||
pkgSearchResult.splice(0, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* create default packages based on `enabled` field and `latest` version.
|
||||
*
|
||||
* @param pkgSearchResults
|
||||
* @param sce angular `$sce` object
|
||||
* @returns {Object} including {name, pkgInfo}
|
||||
*/
|
||||
export function createDefaultPackages(pkgSearchResults, sce) {
|
||||
const defaultPackages = {};
|
||||
// show enabled version if any version of package is enabled
|
||||
for (let name in pkgSearchResults) {
|
||||
const pkgSearchResult = pkgSearchResults[name];
|
||||
defaultPackages[name] = createDefaultPackage(pkgSearchResult, sce)
|
||||
}
|
||||
|
||||
return defaultPackages;
|
||||
}
|
||||
|
|
@ -13,21 +13,32 @@
|
|||
*/
|
||||
|
||||
import { HeliumType, } from './helium-type';
|
||||
import {
|
||||
createAllPackageConfigs,
|
||||
createPersistableConfig,
|
||||
mergePersistedConfWithSpec,
|
||||
} from './helium-conf';
|
||||
import {
|
||||
createDefaultPackages,
|
||||
} from './helium-package';
|
||||
|
||||
angular.module('zeppelinWebApp').service('heliumService', heliumService);
|
||||
|
||||
function heliumService($http, baseUrlSrv, ngToast) {
|
||||
export default function heliumService($http, $sce, baseUrlSrv) {
|
||||
'ngInject';
|
||||
|
||||
var url = baseUrlSrv.getRestApiBase() + '/helium/bundle/load';
|
||||
if (process.env.HELIUM_BUNDLE_DEV) {
|
||||
url = url + '?refresh=true';
|
||||
}
|
||||
|
||||
let visualizationBundles = [];
|
||||
// name `heliumBundles` should be same as `HelumBundleFactory.HELIUM_BUNDLES_VAR`
|
||||
var heliumBundles = [];
|
||||
let heliumBundles = [];
|
||||
// map for `{ magic: interpreter }`
|
||||
let spellPerMagic = {};
|
||||
let visualizationBundles = [];
|
||||
// map for `{ magic: package-name }`
|
||||
let pkgNamePerMagic = {}
|
||||
|
||||
// load should be promise
|
||||
this.load = $http.get(url).success(function(response) {
|
||||
|
|
@ -39,7 +50,9 @@ function heliumService($http, baseUrlSrv, ngToast) {
|
|||
heliumBundles.map(b => {
|
||||
if (b.type === HeliumType.SPELL) {
|
||||
const spell = new b.class(); // eslint-disable-line new-cap
|
||||
const pkgName = b.id;
|
||||
spellPerMagic[spell.getMagic()] = spell;
|
||||
pkgNamePerMagic[spell.getMagic()] = pkgName;
|
||||
} else if (b.type === HeliumType.VISUALIZATION) {
|
||||
visualizationBundles.push(b);
|
||||
}
|
||||
|
|
@ -57,29 +70,54 @@ function heliumService($http, baseUrlSrv, ngToast) {
|
|||
return spellPerMagic[magic];
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Object} map for `{ magic : spell }`
|
||||
*/
|
||||
this.getAllSpells = function() {
|
||||
return spellPerMagic;
|
||||
this.executeSpell = function(magic, textWithoutMagic) {
|
||||
const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
|
||||
.then(confs => createPersistableConfig(confs));
|
||||
|
||||
return promisedConf.then(conf => {
|
||||
const spell = this.getSpellByMagic(magic);
|
||||
const spellResult = spell.interpret(textWithoutMagic, conf);
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(
|
||||
spellPerMagic, magic, textWithoutMagic);
|
||||
|
||||
return parsed;
|
||||
});
|
||||
};
|
||||
|
||||
this.executeSpellAsDisplaySystem = function(magic, textWithoutMagic) {
|
||||
const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
|
||||
.then(confs => createPersistableConfig(confs));
|
||||
|
||||
return promisedConf.then(conf => {
|
||||
const spell = this.getSpellByMagic(magic);
|
||||
const spellResult = spell.interpret(textWithoutMagic.trim(), conf);
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(spellPerMagic);
|
||||
|
||||
return parsed;
|
||||
});
|
||||
};
|
||||
|
||||
this.getVisualizationBundles = function() {
|
||||
return visualizationBundles;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Promise} which returns bundleOrder
|
||||
*/
|
||||
this.getVisualizationPackageOrder = function() {
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/helium/order/visualization');
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/helium/order/visualization')
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Can not get bundle order', error);
|
||||
});
|
||||
};
|
||||
|
||||
this.setVisualizationPackageOrder = function(list) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/order/visualization', list);
|
||||
};
|
||||
|
||||
this.getAllPackageInfo = function() {
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/helium/all');
|
||||
};
|
||||
|
||||
this.enable = function(name, artifact) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact);
|
||||
};
|
||||
|
|
@ -87,4 +125,120 @@ function heliumService($http, baseUrlSrv, ngToast) {
|
|||
this.disable = function(name) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name);
|
||||
};
|
||||
|
||||
this.saveConfig = function(pkg , defaultPackageConfig, closeConfigPanelCallback) {
|
||||
// in case of local package, it will include `/`
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact);
|
||||
const pkgName = pkg.name;
|
||||
const filtered = createPersistableConfig(defaultPackageConfig);
|
||||
|
||||
if (!pkgName || !pkgArtifact|| !filtered) {
|
||||
console.error(
|
||||
`Can't save config for helium package '${pkgArtifact}'`, filtered);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`;
|
||||
return $http.post(url, filtered)
|
||||
.then(() => {
|
||||
if (closeConfigPanelCallback) { closeConfigPanelCallback(); }
|
||||
}).catch((error) => {
|
||||
console.error(`Failed to save config for ${pkgArtifact}`, error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Promise<Object>} which including {name, Array<package info for artifact>}
|
||||
*/
|
||||
this.getAllPackageInfo = function() {
|
||||
return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/package`)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Failed to get all package infos', error);
|
||||
});
|
||||
};
|
||||
|
||||
this.getDefaultPackages = function() {
|
||||
return this.getAllPackageInfo()
|
||||
.then(pkgSearchResults => {
|
||||
return createDefaultPackages(pkgSearchResults, $sce);
|
||||
});
|
||||
};
|
||||
|
||||
this.getAllPackageInfoAndDefaultPackages = function() {
|
||||
return this.getAllPackageInfo()
|
||||
.then(pkgSearchResults => {
|
||||
return {
|
||||
pkgSearchResults: pkgSearchResults,
|
||||
defaultPackages: createDefaultPackages(pkgSearchResults, $sce),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get all package configs.
|
||||
* @return { Promise<{name, Array<Object>}> }
|
||||
*/
|
||||
this.getAllPackageConfigs = function() {
|
||||
const promisedDefaultPackages = this.getDefaultPackages();
|
||||
const promisedPersistedConfs =
|
||||
$http.get(`${baseUrlSrv.getRestApiBase()}/helium/config`)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
|
||||
return Promise.all([promisedDefaultPackages, promisedPersistedConfs])
|
||||
.then(values => {
|
||||
const defaultPackages = values[0];
|
||||
const persistedConfs = values[1];
|
||||
|
||||
return createAllPackageConfigs(defaultPackages, persistedConfs);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Failed to get all package configs', error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get the package config which is persisted in server.
|
||||
* @return { Promise<Array<Object>> }
|
||||
*/
|
||||
this.getSinglePackageConfigs = function(pkg) {
|
||||
const pkgName = pkg.name;
|
||||
// in case of local package, it will include `/`
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact);
|
||||
|
||||
if (!pkgName || !pkgArtifact) {
|
||||
console.error('Failed to fetch config for\n', pkg);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`;
|
||||
const promisedConf = $http.get(confUrl)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
|
||||
return promisedConf.then(({confSpec, confPersisted}) => {
|
||||
const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
|
||||
return merged;
|
||||
});
|
||||
};
|
||||
|
||||
this.getSinglePackageConfigUsingMagic = function(magic) {
|
||||
const pkgName = pkgNamePerMagic[magic];
|
||||
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/spell/config/${pkgName}`;
|
||||
const promisedConf = $http.get(confUrl)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
|
||||
return promisedConf.then(({confSpec, confPersisted}) => {
|
||||
const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
|
||||
return merged;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl);
|
||||
|
||||
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv) {
|
||||
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv, $location, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.SigningIn = false;
|
||||
|
|
@ -37,6 +37,17 @@ function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv)
|
|||
angular.element('#loginModal').modal('toggle');
|
||||
$rootScope.$broadcast('loginSuccess', true);
|
||||
$rootScope.userName = $scope.loginParams.userName;
|
||||
$scope.SigningIn = false;
|
||||
|
||||
//redirect to the page from where the user originally was
|
||||
if ($location.search() && $location.search()['ref']) {
|
||||
$timeout(function() {
|
||||
var redirectLocation = $location.search()['ref'];
|
||||
$location.$$search = {};
|
||||
$location.path(redirectLocation);
|
||||
}, 100);
|
||||
|
||||
}
|
||||
}, function errorCallback(errorResponse) {
|
||||
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.';
|
||||
$scope.SigningIn = false;
|
||||
|
|
@ -51,10 +62,25 @@ function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv)
|
|||
};
|
||||
};
|
||||
|
||||
//handle session logout message received from WebSocket
|
||||
$rootScope.$on('session_logout', function(event, data) {
|
||||
if ($rootScope.userName !== '') {
|
||||
$rootScope.userName = '';
|
||||
$rootScope.ticket = undefined;
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.loginParams = {};
|
||||
$scope.loginParams.errorText = data.info;
|
||||
angular.element('.nav-login-btn').click();
|
||||
}, 1000);
|
||||
var locationPath = $location.path();
|
||||
$location.path('/').search('ref', locationPath);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('initLoginValues', function() {
|
||||
initValues();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
|
|||
}
|
||||
}]
|
||||
});
|
||||
} else if (op === 'SESSION_LOGOUT') {
|
||||
$rootScope.$broadcast('session_logout', data);
|
||||
} else if (op === 'CONFIGURATIONS_INFO') {
|
||||
$rootScope.$broadcast('configurationsInfo', data);
|
||||
} else if (op === 'INTERPRETER_SETTINGS') {
|
||||
|
|
@ -166,6 +168,8 @@ function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
|
|||
$rootScope.$broadcast('setNoteRevisionResult', data);
|
||||
} else if (op === 'PARAS_INFO') {
|
||||
$rootScope.$broadcast('updateParaInfos', data);
|
||||
} else {
|
||||
console.error(`unknown websocket op: ${op}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ function websocketMsgSrv($rootScope, websocketEvents) {
|
|||
|
||||
getInterpreterSettings: function() {
|
||||
websocketEvents.sendNewEvent({op: 'GET_INTERPRETER_SETTINGS'});
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import './app/home/home.controller.js';
|
|||
import './app/handsontable/handsonHelper.js';
|
||||
import './app/notebook/notebook.controller.js';
|
||||
|
||||
/** start: global variable `zeppelin` related files */
|
||||
import './app/tabledata/tabledata.js';
|
||||
import './app/tabledata/transformation.js';
|
||||
import './app/tabledata/pivot.js';
|
||||
|
|
@ -32,7 +31,6 @@ import './app/visualization/builtins/visualization-piechart.js';
|
|||
import './app/visualization/builtins/visualization-areachart.js';
|
||||
import './app/visualization/builtins/visualization-linechart.js';
|
||||
import './app/visualization/builtins/visualization-scatterchart.js';
|
||||
/** end: global variable `zeppelin` related files */
|
||||
|
||||
import './app/jobmanager/jobmanager.controller.js';
|
||||
import './app/jobmanager/jobs/job.controller.js';
|
||||
|
|
@ -45,7 +43,7 @@ import './app/notebook/paragraph/paragraph.controller.js';
|
|||
import './app/notebook/paragraph/result/result.controller.js';
|
||||
import './app/search/result-list.controller.js';
|
||||
import './app/notebookRepos/notebookRepos.controller.js';
|
||||
import './app/helium/helium.controller.js';
|
||||
import './app/helium';
|
||||
import './components/arrayOrderingSrv/arrayOrdering.service.js';
|
||||
import './components/clipboard/clipboard.controller.js';
|
||||
import './components/navbar/navbar.controller.js';
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"esnext": true,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"quotmark": "single",
|
||||
"regexp": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"globals": {
|
||||
"after": false,
|
||||
"afterEach": false,
|
||||
"angular": false,
|
||||
"before": false,
|
||||
"beforeEach": false,
|
||||
"browser": false,
|
||||
"describe": false,
|
||||
"expect": false,
|
||||
"inject": false,
|
||||
"it": false,
|
||||
"jasmine": false,
|
||||
"spyOn": false,
|
||||
"zeppelin" : false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -19,6 +19,7 @@ package org.apache.zeppelin.helium;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
import org.apache.zeppelin.resource.DistributedResourcePool;
|
||||
|
|
@ -48,6 +49,8 @@ public class Helium {
|
|||
private final HeliumBundleFactory bundleFactory;
|
||||
private final HeliumApplicationFactory applicationFactory;
|
||||
|
||||
Map<String, List<HeliumPackageSearchResult>> allPackages;
|
||||
|
||||
public Helium(
|
||||
String heliumConfPath,
|
||||
String registryPaths,
|
||||
|
|
@ -142,7 +145,7 @@ public class Helium {
|
|||
}
|
||||
|
||||
private void clearNotExistsPackages() {
|
||||
Map<String, List<HeliumPackageSearchResult>> all = getAllPackageInfo();
|
||||
Map<String, List<HeliumPackageSearchResult>> all = getAllPackageInfoWithoutRefresh();
|
||||
|
||||
// clear visualization display order
|
||||
List<String> packageOrder = heliumConf.getBundleDisplayOrder();
|
||||
|
|
@ -163,47 +166,110 @@ public class Helium {
|
|||
}
|
||||
}
|
||||
|
||||
public Map<String, List<HeliumPackageSearchResult>> getAllPackageInfoWithoutRefresh() {
|
||||
return getAllPackageInfo(false, null);
|
||||
}
|
||||
|
||||
public Map<String, List<HeliumPackageSearchResult>> getAllPackageInfo() {
|
||||
return getAllPackageInfo(true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refresh
|
||||
* @param packageName
|
||||
*/
|
||||
public Map<String, List<HeliumPackageSearchResult>> getAllPackageInfo(boolean refresh,
|
||||
String packageName) {
|
||||
Map<String, String> enabledPackageInfo = heliumConf.getEnabledPackages();
|
||||
|
||||
Map<String, List<HeliumPackageSearchResult>> map = new HashMap<>();
|
||||
synchronized (registry) {
|
||||
for (HeliumRegistry r : registry) {
|
||||
try {
|
||||
for (HeliumPackage pkg : r.getAll()) {
|
||||
String name = pkg.getName();
|
||||
String artifact = enabledPackageInfo.get(name);
|
||||
boolean enabled = (artifact != null && artifact.equals(pkg.getArtifact()));
|
||||
if (refresh || allPackages == null) {
|
||||
allPackages = new HashMap<>();
|
||||
for (HeliumRegistry r : registry) {
|
||||
try {
|
||||
for (HeliumPackage pkg : r.getAll()) {
|
||||
String name = pkg.getName();
|
||||
|
||||
if (!map.containsKey(name)) {
|
||||
map.put(name, new LinkedList<HeliumPackageSearchResult>());
|
||||
if (!StringUtils.isEmpty(packageName) &&
|
||||
!name.equals(packageName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String artifact = enabledPackageInfo.get(name);
|
||||
boolean enabled = (artifact != null && artifact.equals(pkg.getArtifact()));
|
||||
|
||||
if (!allPackages.containsKey(name)) {
|
||||
allPackages.put(name, new LinkedList<HeliumPackageSearchResult>());
|
||||
}
|
||||
allPackages.get(name).add(new HeliumPackageSearchResult(r.name(), pkg, enabled));
|
||||
}
|
||||
map.get(name).add(new HeliumPackageSearchResult(r.name(), pkg, enabled));
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
for (String name : allPackages.keySet()) {
|
||||
if (!StringUtils.isEmpty(packageName) &&
|
||||
!name.equals(packageName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<HeliumPackageSearchResult> pkgs = allPackages.get(name);
|
||||
String artifact = enabledPackageInfo.get(name);
|
||||
LinkedList<HeliumPackageSearchResult> newResults =
|
||||
new LinkedList<HeliumPackageSearchResult>();
|
||||
|
||||
for (HeliumPackageSearchResult pkg : pkgs) {
|
||||
boolean enabled = (artifact != null && artifact.equals(pkg.getPkg().getArtifact()));
|
||||
newResults.add(new HeliumPackageSearchResult(pkg.getRegistry(), pkg.getPkg(), enabled));
|
||||
}
|
||||
|
||||
allPackages.put(name, newResults);
|
||||
}
|
||||
}
|
||||
|
||||
// sort version (artifact)
|
||||
for (String name : allPackages.keySet()) {
|
||||
List<HeliumPackageSearchResult> packages = allPackages.get(name);
|
||||
Collections.sort(packages, new Comparator<HeliumPackageSearchResult>() {
|
||||
@Override
|
||||
public int compare(HeliumPackageSearchResult o1, HeliumPackageSearchResult o2) {
|
||||
return o2.getPkg().getArtifact().compareTo(o1.getPkg().getArtifact());
|
||||
}
|
||||
});
|
||||
}
|
||||
return allPackages;
|
||||
}
|
||||
}
|
||||
|
||||
public List<HeliumPackageSearchResult> getSinglePackageInfo(String packageName) {
|
||||
Map<String, List<HeliumPackageSearchResult>> result = getAllPackageInfo(false, packageName);
|
||||
|
||||
if (!result.containsKey(packageName)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return result.get(packageName);
|
||||
}
|
||||
|
||||
public HeliumPackageSearchResult getEnabledPackageInfo(String packageName) {
|
||||
Map<String, List<HeliumPackageSearchResult>> infos = getAllPackageInfoWithoutRefresh();
|
||||
List<HeliumPackageSearchResult> packages = infos.get(packageName);
|
||||
|
||||
for (HeliumPackageSearchResult pkgSearchResult : packages) {
|
||||
if (pkgSearchResult.isEnabled()) {
|
||||
return pkgSearchResult;
|
||||
}
|
||||
}
|
||||
|
||||
// sort version (artifact)
|
||||
for (String name : map.keySet()) {
|
||||
List<HeliumPackageSearchResult> packages = map.get(name);
|
||||
Collections.sort(packages, new Comparator<HeliumPackageSearchResult>() {
|
||||
@Override
|
||||
public int compare(HeliumPackageSearchResult o1, HeliumPackageSearchResult o2) {
|
||||
return o2.getPkg().getArtifact().compareTo(o1.getPkg().getArtifact());
|
||||
}
|
||||
});
|
||||
}
|
||||
return map;
|
||||
return null;
|
||||
}
|
||||
|
||||
public HeliumPackageSearchResult getPackageInfo(String name, String artifact) {
|
||||
Map<String, List<HeliumPackageSearchResult>> infos = getAllPackageInfo();
|
||||
List<HeliumPackageSearchResult> packages = infos.get(name);
|
||||
public HeliumPackageSearchResult getPackageInfo(String pkgName, String artifact) {
|
||||
Map<String, List<HeliumPackageSearchResult>> infos = getAllPackageInfo(false, pkgName);
|
||||
List<HeliumPackageSearchResult> packages = infos.get(pkgName);
|
||||
if (artifact == null) {
|
||||
return packages.get(0);
|
||||
return packages.get(0); /** return the FIRST package */
|
||||
} else {
|
||||
for (HeliumPackageSearchResult pkg : packages) {
|
||||
if (pkg.getPkg().getArtifact().equals(artifact)) {
|
||||
|
|
@ -255,6 +321,21 @@ public class Helium {
|
|||
save();
|
||||
}
|
||||
|
||||
public void updatePackageConfig(String artifact, Map<String, Object> pkgConfig)
|
||||
throws IOException {
|
||||
|
||||
heliumConf.updatePackageConfig(artifact, pkgConfig);
|
||||
save();
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Object>> getAllPackageConfig() {
|
||||
return heliumConf.getAllPackageConfigs();
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackagePersistedConfig(String artifact) {
|
||||
return heliumConf.getPackagePersistedConfig(artifact);
|
||||
}
|
||||
|
||||
public HeliumPackageSuggestion suggestApp(Paragraph paragraph) {
|
||||
HeliumPackageSuggestion suggestion = new HeliumPackageSuggestion();
|
||||
|
||||
|
|
@ -276,7 +357,7 @@ public class Helium {
|
|||
allResources = ResourcePoolUtils.getAllResources();
|
||||
}
|
||||
|
||||
for (List<HeliumPackageSearchResult> pkgs : getAllPackageInfo().values()) {
|
||||
for (List<HeliumPackageSearchResult> pkgs : getAllPackageInfoWithoutRefresh().values()) {
|
||||
for (HeliumPackageSearchResult pkg : pkgs) {
|
||||
if (pkg.getPkg().getType() == HeliumType.APPLICATION && pkg.isEnabled()) {
|
||||
ResourceSet resources = ApplicationLoader.findRequiredResourceSet(
|
||||
|
|
@ -304,7 +385,7 @@ public class Helium {
|
|||
* @return ordered list of enabled buildBundle package
|
||||
*/
|
||||
public List<HeliumPackage> getBundlePackagesToBundle() {
|
||||
Map<String, List<HeliumPackageSearchResult>> allPackages = getAllPackageInfo();
|
||||
Map<String, List<HeliumPackageSearchResult>> allPackages = getAllPackageInfoWithoutRefresh();
|
||||
List<String> visOrder = heliumConf.getBundleDisplayOrder();
|
||||
|
||||
List<HeliumPackage> orderedBundlePackages = new LinkedList<>();
|
||||
|
|
@ -368,4 +449,51 @@ public class Helium {
|
|||
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packageName
|
||||
* @return { "confPersisted", "confSpec" } or return null if failed to found enabled package
|
||||
*/
|
||||
public Map<String, Map<String, Object>> getSpellConfig(String packageName) {
|
||||
HeliumPackageSearchResult result = getEnabledPackageInfo(packageName);
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HeliumPackage enabledPackage = result.getPkg();
|
||||
|
||||
Map<String, Object> configSpec = enabledPackage.getConfig();
|
||||
Map<String, Object> configPersisted =
|
||||
getPackagePersistedConfig(enabledPackage.getArtifact());
|
||||
|
||||
return createMixedConfig(configPersisted, configSpec);
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Object>> getPackageConfig(String pkgName,
|
||||
String artifact) {
|
||||
|
||||
HeliumPackageSearchResult result = getPackageInfo(pkgName, artifact);
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HeliumPackage requestedPackage = result.getPkg();
|
||||
|
||||
Map<String, Object> configSpec = requestedPackage.getConfig();
|
||||
Map<String, Object> configPersisted =
|
||||
getPackagePersistedConfig(artifact);
|
||||
|
||||
return createMixedConfig(configPersisted, configSpec);
|
||||
}
|
||||
|
||||
public static Map<String, Map<String, Object>> createMixedConfig(Map<String, Object> persisted,
|
||||
Map<String, Object> spec) {
|
||||
Map<String, Map<String, Object>> mixed = new HashMap<>();
|
||||
mixed.put("confPersisted", persisted);
|
||||
mixed.put("confSpec", spec);
|
||||
|
||||
return mixed;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,19 @@ package org.apache.zeppelin.helium;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Helium config. This object will be persisted to conf/heliumc.conf
|
||||
* Helium config. This object will be persisted to conf/helium.conf
|
||||
*/
|
||||
public class HeliumConf {
|
||||
// enabled packages {name, version}
|
||||
Map<String, String> enabled = Collections.synchronizedMap(new HashMap<String, String>());
|
||||
private Map<String, String> enabled = Collections.synchronizedMap(new HashMap<String, String>());
|
||||
|
||||
// {artifact, {configKey, configValue}}
|
||||
private Map<String, Map<String, Object>> packageConfig =
|
||||
Collections.synchronizedMap(
|
||||
new HashMap<String, Map<String, Object>>());
|
||||
|
||||
// enabled visualization package display order
|
||||
List<String> bundleDisplayOrder = new LinkedList<>();
|
||||
private List<String> bundleDisplayOrder = new LinkedList<>();
|
||||
|
||||
public Map<String, String> getEnabledPackages() {
|
||||
return new HashMap<>(enabled);
|
||||
|
|
@ -40,6 +45,32 @@ public class HeliumConf {
|
|||
enabled.put(name, artifact);
|
||||
}
|
||||
|
||||
public void updatePackageConfig(String artifact,
|
||||
Map<String, Object> newConfig) {
|
||||
if (!packageConfig.containsKey(artifact)) {
|
||||
packageConfig.put(artifact,
|
||||
Collections.synchronizedMap(new HashMap<String, Object>()));
|
||||
}
|
||||
|
||||
packageConfig.put(artifact, newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return versioned package config `{artifact, {configKey, configVal}}`
|
||||
*/
|
||||
public Map<String, Map<String, Object>> getAllPackageConfigs () {
|
||||
return packageConfig;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackagePersistedConfig(String artifact) {
|
||||
if (!packageConfig.containsKey(artifact)) {
|
||||
packageConfig.put(artifact,
|
||||
Collections.synchronizedMap(new HashMap<String, Object>()));
|
||||
}
|
||||
|
||||
return packageConfig.get(artifact);
|
||||
}
|
||||
|
||||
public void disablePackage(HeliumPackage pkg) {
|
||||
disablePackage(pkg.getName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class InterpreterInfo {
|
|||
private boolean defaultInterpreter = false;
|
||||
private Map<String, Object> editor;
|
||||
|
||||
InterpreterInfo(String className, String name, boolean defaultInterpreter,
|
||||
public InterpreterInfo(String className, String name, boolean defaultInterpreter,
|
||||
Map<String, Object> editor) {
|
||||
this.className = className;
|
||||
this.name = name;
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ public class InterpreterSettingManager {
|
|||
setting.getId());
|
||||
if (localRepoDir.exists()) {
|
||||
try {
|
||||
FileUtils.cleanDirectory(localRepoDir);
|
||||
FileUtils.forceDelete(localRepoDir);
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.info("A file that does not exist cannot be deleted, nothing to worry", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,15 @@ public class Note implements Serializable, ParagraphJobListener {
|
|||
valueString = "false";
|
||||
}
|
||||
getConfig().put("personalizedMode", valueString);
|
||||
clearUserParagraphs(value);
|
||||
}
|
||||
|
||||
private void clearUserParagraphs(boolean isPersonalized) {
|
||||
if (!isPersonalized) {
|
||||
for (Paragraph p : paragraphs) {
|
||||
p.clearUserParagraphs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
|
@ -351,6 +360,7 @@ public class Note implements Serializable, ParagraphJobListener {
|
|||
synchronized (paragraphs) {
|
||||
paragraphs.add(index, p);
|
||||
}
|
||||
p.addUser(p, p.getUser());
|
||||
if (noteEventListener != null) {
|
||||
noteEventListener.onParagraphCreate(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
* Paragraph is a representation of an execution unit.
|
||||
*/
|
||||
public class Paragraph extends Job implements Serializable, Cloneable {
|
||||
|
||||
private static final long serialVersionUID = -6328572073497992016L;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(Paragraph.class);
|
||||
|
|
@ -124,6 +125,9 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
}
|
||||
|
||||
public Paragraph getUserParagraph(String user) {
|
||||
if (!userParagraphMap.containsKey(user)) {
|
||||
cloneParagraphForUser(user);
|
||||
}
|
||||
return userParagraphMap.get(user);
|
||||
}
|
||||
|
||||
|
|
@ -140,14 +144,20 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
p.setTitle(getTitle());
|
||||
p.setText(getText());
|
||||
p.setResult(getReturn());
|
||||
p.setStatus(getStatus());
|
||||
p.setStatus(Status.READY);
|
||||
p.setId(getId());
|
||||
|
||||
userParagraphMap.put(user, p);
|
||||
|
||||
addUser(p, user);
|
||||
return p;
|
||||
}
|
||||
|
||||
public void clearUserParagraphs() {
|
||||
userParagraphMap.clear();
|
||||
}
|
||||
|
||||
public void addUser(Paragraph p, String user) {
|
||||
userParagraphMap.put(user, p);
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
|
@ -369,6 +379,10 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
}
|
||||
}
|
||||
|
||||
for (Paragraph p : userParagraphMap.values()) {
|
||||
p.setText(getText());
|
||||
}
|
||||
|
||||
String script = getScriptBody();
|
||||
// inject form
|
||||
if (repl.getFormType() == FormType.NATIVE) {
|
||||
|
|
@ -400,13 +414,9 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
List<InterpreterResultMessage> resultMessages = context.out.toInterpreterResultMessage();
|
||||
resultMessages.addAll(ret.message());
|
||||
|
||||
for (Paragraph p : userParagraphMap.values()) {
|
||||
p.setText(getText());
|
||||
}
|
||||
|
||||
InterpreterResult res = new InterpreterResult(ret.code(), resultMessages);
|
||||
|
||||
Paragraph p = userParagraphMap.get(getUser());
|
||||
Paragraph p = getUserParagraph(getUser());
|
||||
if (null != p) {
|
||||
p.setResult(res);
|
||||
p.settings.setParams(settings.getParams());
|
||||
|
|
@ -525,12 +535,12 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
Credentials credentials = note.getCredentials();
|
||||
if (authenticationInfo != null) {
|
||||
UserCredentials userCredentials =
|
||||
credentials.getUserCredentials(authenticationInfo.getUser());
|
||||
credentials.getUserCredentials(authenticationInfo.getUser());
|
||||
authenticationInfo.setUserCredentials(userCredentials);
|
||||
}
|
||||
|
||||
InterpreterContext interpreterContext =
|
||||
new InterpreterContext(note.getId(), getId(), getRequiredReplName(), this.getTitle(),
|
||||
new InterpreterContext(note.getId(), getId(), getRequiredReplName(), this.getTitle(),
|
||||
this.getText(), this.getAuthenticationInfo(), this.getConfig(), this.settings, registry,
|
||||
resourcePool, runners, output);
|
||||
return interpreterContext;
|
||||
|
|
@ -573,7 +583,15 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
return new ParagraphRunner(note, note.getId(), getId());
|
||||
}
|
||||
|
||||
public void setStatusToUserParagraph(Status status) {
|
||||
String user = getUser();
|
||||
if (null != user) {
|
||||
getUserParagraph(getUser()).setStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
static class ParagraphRunner extends InterpreterContextRunner {
|
||||
|
||||
private transient Note note;
|
||||
|
||||
public ParagraphRunner(Note note, String noteId, String paragraphId) {
|
||||
|
|
@ -698,7 +716,7 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
for (String key : infos.keySet()) {
|
||||
ParagraphRuntimeInfo info = this.runtimeInfos.get(key);
|
||||
if (info == null) {
|
||||
info = new ParagraphRuntimeInfo(key, label, tooltip, group, intpSettingId);
|
||||
info = new ParagraphRuntimeInfo(key, label, tooltip, group, intpSettingId);
|
||||
this.runtimeInfos.put(key, info);
|
||||
}
|
||||
info.addValue(infos.get(key));
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ public class Message {
|
|||
GET_INTERPRETER_SETTINGS, // [c-s] get interpreter settings
|
||||
INTERPRETER_SETTINGS, // [s-c] interpreter settings
|
||||
ERROR_INFO, // [s-c] error information to be sent
|
||||
SESSION_LOGOUT, // [s-c] error information to be sent
|
||||
WATCHER, // [s-c] Change websocket to watcher mode.
|
||||
PARAGRAPH_ADDED, // [s-c] paragraph is added
|
||||
PARAGRAPH_REMOVED, // [s-c] paragraph deleted
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
*/
|
||||
package org.apache.zeppelin.helium;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.dep.Dependency;
|
||||
import org.apache.zeppelin.dep.DependencyResolver;
|
||||
import org.apache.zeppelin.interpreter.*;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
|
||||
|
|
@ -35,9 +37,11 @@ import org.junit.Test;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
|
@ -73,16 +77,11 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
|
|||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName(), home.getAbsolutePath());
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_CONF_DIR.getVarName(), tmpDir.getAbsolutePath() + "/conf");
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath());
|
||||
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
conf = new ZeppelinConfiguration();
|
||||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
|
||||
MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
|
||||
heliumAppFactory = new HeliumApplicationFactory();
|
||||
depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo");
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
|
|
@ -91,6 +90,18 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
|
|||
env.put("ZEPPELIN_CLASSPATH", new File("./target/test-classes").getAbsolutePath());
|
||||
factory.setEnv(env);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), new Properties());
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>();
|
||||
interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock2", interpreterInfos2, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock2", null);
|
||||
interpreterSettingManager.createNewSetting("mock2", "mock2", new ArrayList<Dependency>(), new InterpreterOption(), new Properties());
|
||||
|
||||
SearchService search = mock(SearchService.class);
|
||||
notebookRepo = new VFSNotebookRepo(conf);
|
||||
NotebookAuthorization notebookAuthorization = NotebookAuthorization.init(conf);
|
||||
|
|
|
|||
|
|
@ -100,4 +100,43 @@ public class HeliumTest {
|
|||
// then
|
||||
assertEquals(2, helium.getAllPackageInfo().size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRefresh() throws IOException, URISyntaxException, TaskRunnerException {
|
||||
File heliumConf = new File(tmpDir, "helium.conf");
|
||||
Helium helium = new Helium(
|
||||
heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null);
|
||||
HeliumTestRegistry registry1 = new HeliumTestRegistry("r1", "r1");
|
||||
helium.addRegistry(registry1);
|
||||
|
||||
// when
|
||||
registry1.add(new HeliumPackage(
|
||||
HeliumType.APPLICATION,
|
||||
"name1",
|
||||
"desc1",
|
||||
"artifact1",
|
||||
"className1",
|
||||
new String[][]{},
|
||||
"",
|
||||
""));
|
||||
|
||||
// then
|
||||
assertEquals(1, helium.getAllPackageInfoWithoutRefresh().size());
|
||||
|
||||
// when
|
||||
registry1.add(new HeliumPackage(
|
||||
HeliumType.APPLICATION,
|
||||
"name2",
|
||||
"desc2",
|
||||
"artifact2",
|
||||
"className2",
|
||||
new String[][]{},
|
||||
"",
|
||||
""));
|
||||
|
||||
// then
|
||||
assertEquals(1, helium.getAllPackageInfoWithoutRefresh().size());
|
||||
assertEquals(2, helium.getAllPackageInfo(true, null).size());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import java.util.Map;
|
|||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.NullArgumentException;
|
||||
|
|
@ -87,17 +88,7 @@ public class InterpreterFactoryTest {
|
|||
new File(tmpDir, "conf").mkdirs();
|
||||
FileUtils.copyDirectory(new File("src/test/resources/interpreter"), new File(tmpDir, "interpreter"));
|
||||
|
||||
Map<String, InterpreterProperty> propertiesMockInterpreter1 = new HashMap<>();
|
||||
propertiesMockInterpreter1.put("PROPERTY_1", new InterpreterProperty("PROPERTY_1", "", "VALUE_1", "desc"));
|
||||
propertiesMockInterpreter1.put("property_2", new InterpreterProperty("", "property_2", "value_2", "desc"));
|
||||
MockInterpreter1.register("mock1", "mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1", propertiesMockInterpreter1);
|
||||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(),
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter1," +
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter2," +
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter11");
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETER_GROUP_ORDER.getVarName(),
|
||||
"mock1,mock2,mock11,dev");
|
||||
conf = new ZeppelinConfiguration();
|
||||
|
|
@ -107,6 +98,21 @@ public class InterpreterFactoryTest {
|
|||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
context = new InterpreterContext("note", "id", null, "title", "text", null, null, null, null, null, null, null);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
Properties intp1Properties = new Properties();
|
||||
intp1Properties.put("PROPERTY_1", "VALUE_1");
|
||||
intp1Properties.put("property_2", "value_2");
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>();
|
||||
interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock2", interpreterInfos2, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock2", null);
|
||||
interpreterSettingManager.createNewSetting("mock2", "mock2", new ArrayList<Dependency>(), new InterpreterOption(), new Properties());
|
||||
|
||||
SearchService search = mock(SearchService.class);
|
||||
notebookRepo = new VFSNotebookRepo(conf);
|
||||
notebookAuthorization = NotebookAuthorization.init(conf);
|
||||
|
|
@ -149,6 +155,14 @@ public class InterpreterFactoryTest {
|
|||
@Test
|
||||
public void testRemoteRepl() throws Exception {
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
Properties intp1Properties = new Properties();
|
||||
intp1Properties.put("PROPERTY_1", "VALUE_1");
|
||||
intp1Properties.put("property_2", "value_2");
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties);
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
List<InterpreterSetting> all = interpreterSettingManager.get();
|
||||
InterpreterSetting mock1Setting = null;
|
||||
|
|
@ -178,6 +192,14 @@ public class InterpreterFactoryTest {
|
|||
@Test
|
||||
public void testRestartInterpreterInScopedMode() throws Exception {
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
Properties intp1Properties = new Properties();
|
||||
intp1Properties.put("PROPERTY_1", "VALUE_1");
|
||||
intp1Properties.put("property_2", "value_2");
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties);
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
List<InterpreterSetting> all = interpreterSettingManager.get();
|
||||
InterpreterSetting mock1Setting = null;
|
||||
|
|
@ -215,6 +237,14 @@ public class InterpreterFactoryTest {
|
|||
@Test
|
||||
public void testRestartInterpreterInIsolatedMode() throws Exception {
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
Properties intp1Properties = new Properties();
|
||||
intp1Properties.put("PROPERTY_1", "VALUE_1");
|
||||
intp1Properties.put("property_2", "value_2");
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties);
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
List<InterpreterSetting> all = interpreterSettingManager.get();
|
||||
InterpreterSetting mock1Setting = null;
|
||||
|
|
@ -282,7 +312,13 @@ public class InterpreterFactoryTest {
|
|||
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
|
||||
assertEquals(numInterpreters + 1, interpreterSettingManager.get().size());
|
||||
/*
|
||||
Current situation, if InterpreterSettinfRef doesn't have the key of InterpreterSetting, it would be ignored.
|
||||
Thus even though interpreter.json have several interpreterSetting in that file, it would be ignored and would not be initialized from loadFromFile.
|
||||
In this case, only "mock11" would be referenced from file under interpreter/mock, and "mock11" group would be initialized.
|
||||
*/
|
||||
// TODO(jl): Decide how to handle the know referenced interpreterSetting.
|
||||
assertEquals(1, interpreterSettingManager.get().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -18,29 +18,30 @@ package org.apache.zeppelin.notebook;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
||||
import org.apache.zeppelin.dep.Dependency;
|
||||
import org.apache.zeppelin.dep.DependencyResolver;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterFactory;
|
||||
import org.apache.zeppelin.interpreter.InterpreterInfo;
|
||||
import org.apache.zeppelin.interpreter.InterpreterOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterProperty;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSettingManager;
|
||||
import org.apache.zeppelin.interpreter.LazyOpenInterpreter;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter11;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter2;
|
||||
import org.apache.zeppelin.interpreter.remote.RemoteInterpreter;
|
||||
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class NoteInterpreterLoaderTest {
|
||||
|
|
@ -61,15 +62,23 @@ public class NoteInterpreterLoaderTest {
|
|||
|
||||
conf = ZeppelinConfiguration.create();
|
||||
|
||||
Interpreter.registeredInterpreters = Collections
|
||||
.synchronizedMap(new HashMap<String, Interpreter.RegisteredInterpreter>());
|
||||
MockInterpreter1.register("mock1", "group1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1", true);
|
||||
MockInterpreter11.register("mock11", "group1", "org.apache.zeppelin.interpreter.mock.MockInterpreter11");
|
||||
MockInterpreter2.register("mock2", "group2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo");
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, Maps.<String, Object>newHashMap()));
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter11.class.getName(), "mock11", false, Maps.<String, Object>newHashMap()));
|
||||
ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>();
|
||||
interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, Maps.<String, Object>newHashMap()));
|
||||
|
||||
interpreterSettingManager.add("group1", interpreterInfos, Lists.<Dependency>newArrayList(), new InterpreterOption(), Maps.<String, InterpreterProperty>newHashMap(), "mock", null);
|
||||
interpreterSettingManager.add("group2", interpreterInfos2, Lists.<Dependency>newArrayList(), new InterpreterOption(), Maps.<String, InterpreterProperty>newHashMap(), "mock", null);
|
||||
|
||||
interpreterSettingManager.createNewSetting("group1", "group1", Lists.<Dependency>newArrayList(), new InterpreterOption(), new Properties());
|
||||
interpreterSettingManager.createNewSetting("group2", "group2", Lists.<Dependency>newArrayList(), new InterpreterOption(), new Properties());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
|||
|
|
@ -130,6 +130,13 @@ public class NoteTest {
|
|||
assertNull(p2.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertParagraphwithUser() {
|
||||
Note note = new Note(repo, interpreterFactory, interpreterSettingManager, jobListenerFactory, index, credentials, noteEventListener);
|
||||
Paragraph p = note.insertParagraph(note.getParagraphs().size(), AuthenticationInfo.ANONYMOUS);
|
||||
assertEquals("anonymous", p.getUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearAllParagraphOutputTest() {
|
||||
when(interpreterFactory.getInterpreter(anyString(), anyString(), eq("md"))).thenReturn(interpreter);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package org.apache.zeppelin.notebook;
|
|||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
|
@ -31,6 +32,7 @@ import com.google.common.collect.Sets;
|
|||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
||||
import org.apache.zeppelin.dep.Dependency;
|
||||
import org.apache.zeppelin.dep.DependencyResolver;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.interpreter.*;
|
||||
|
|
@ -83,19 +85,28 @@ public class NotebookTest implements JobListenerFactory{
|
|||
System.setProperty(ConfVars.ZEPPELIN_CONF_DIR.getVarName(), tmpDir.toString() + "/conf");
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
conf = ZeppelinConfiguration.create();
|
||||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
|
||||
MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo");
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(false));
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(), new Properties());
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>();
|
||||
interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock2", interpreterInfos2, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock2", null);
|
||||
interpreterSettingManager.createNewSetting("mock2", "mock2", new ArrayList<Dependency>(), new InterpreterOption(), new Properties());
|
||||
|
||||
|
||||
SearchService search = mock(SearchService.class);
|
||||
notebookRepo = new VFSNotebookRepo(conf);
|
||||
notebookAuthorization = NotebookAuthorization.init(conf);
|
||||
|
|
@ -103,6 +114,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
|
||||
notebook = new Notebook(conf, notebookRepo, schedulerFactory, factory, interpreterSettingManager, this, search,
|
||||
notebookAuthorization, credentials);
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_PUBLIC.getVarName(), "true");
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -1148,7 +1160,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
assertEquals(notebookAuthorization.getOwners(notePublic.getId()).size(), 1);
|
||||
assertEquals(notebookAuthorization.getReaders(notePublic.getId()).size(), 0);
|
||||
assertEquals(notebookAuthorization.getWriters(notePublic.getId()).size(), 0);
|
||||
|
||||
|
||||
// case of private note
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_PUBLIC.getVarName(), "false");
|
||||
ZeppelinConfiguration conf2 = ZeppelinConfiguration.create();
|
||||
|
|
@ -1164,13 +1176,13 @@ public class NotebookTest implements JobListenerFactory{
|
|||
|
||||
// create private note
|
||||
Note notePrivate = notebook.createNote(new AuthenticationInfo("user1"));
|
||||
|
||||
|
||||
// only user1 have notePrivate right after creation
|
||||
notes1 = notebook.getAllNotes(user1);
|
||||
notes2 = notebook.getAllNotes(user2);
|
||||
assertEquals(notes1.size(), 2);
|
||||
assertEquals(notes2.size(), 1);
|
||||
assertEquals(notes1.get(1).getId(), notePrivate.getId());
|
||||
assertEquals(true, notes1.contains(notePrivate));
|
||||
|
||||
// user1 have all rights
|
||||
assertEquals(notebookAuthorization.getOwners(notePrivate.getId()).size(), 1);
|
||||
|
|
|
|||
|
|
@ -19,22 +19,48 @@ package org.apache.zeppelin.notebook;
|
|||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import org.apache.zeppelin.display.AngularObject;
|
||||
import org.apache.zeppelin.display.AngularObjectBuilder;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.Input;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.Interpreter.FormType;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterFactory;
|
||||
import org.apache.zeppelin.interpreter.InterpreterGroup;
|
||||
import org.apache.zeppelin.interpreter.InterpreterOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResultMessage;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting.Status;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSettingManager;
|
||||
import org.apache.zeppelin.resource.ResourcePool;
|
||||
import org.apache.zeppelin.scheduler.JobListener;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.apache.zeppelin.user.Credentials;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class ParagraphTest {
|
||||
@Test
|
||||
|
|
@ -125,4 +151,85 @@ public class ParagraphTest {
|
|||
verify(registry).get("age", noteId, null);
|
||||
assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnDefaultParagraphWithNewUser() {
|
||||
Paragraph p = new Paragraph("para_1", null, null, null, null);
|
||||
Object defaultValue = "Default Value";
|
||||
p.setResult(defaultValue);
|
||||
Paragraph newUserParagraph = p.getUserParagraph("new_user");
|
||||
assertNotNull(newUserParagraph);
|
||||
assertEquals(defaultValue, newUserParagraph.getReturn());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnUnchangedResultsWithDifferentUser() throws Throwable {
|
||||
InterpreterSettingManager mockInterpreterSettingManager = mock(InterpreterSettingManager.class);
|
||||
Note mockNote = mock(Note.class);
|
||||
when(mockNote.getCredentials()).thenReturn(mock(Credentials.class));
|
||||
Paragraph spyParagraph = spy(new Paragraph("para_1", mockNote, null, null, mockInterpreterSettingManager));
|
||||
|
||||
doReturn("spy").when(spyParagraph).getRequiredReplName();
|
||||
|
||||
|
||||
Interpreter mockInterpreter = mock(Interpreter.class);
|
||||
doReturn(mockInterpreter).when(spyParagraph).getRepl(anyString());
|
||||
|
||||
InterpreterGroup mockInterpreterGroup = mock(InterpreterGroup.class);
|
||||
when(mockInterpreter.getInterpreterGroup()).thenReturn(mockInterpreterGroup);
|
||||
when(mockInterpreterGroup.getId()).thenReturn("mock_id_1");
|
||||
when(mockInterpreterGroup.getAngularObjectRegistry()).thenReturn(mock(AngularObjectRegistry.class));
|
||||
when(mockInterpreterGroup.getResourcePool()).thenReturn(mock(ResourcePool.class));
|
||||
|
||||
List<InterpreterSetting> spyInterpreterSettingList = spy(Lists.<InterpreterSetting>newArrayList());
|
||||
InterpreterSetting mockInterpreterSetting = mock(InterpreterSetting.class);
|
||||
InterpreterOption mockInterpreterOption = mock(InterpreterOption.class);
|
||||
when(mockInterpreterSetting.getOption()).thenReturn(mockInterpreterOption);
|
||||
when(mockInterpreterOption.permissionIsSet()).thenReturn(false);
|
||||
when(mockInterpreterSetting.getStatus()).thenReturn(Status.READY);
|
||||
when(mockInterpreterSetting.getId()).thenReturn("mock_id_1");
|
||||
when(mockInterpreterSetting.getInterpreterGroup(anyString(), anyString())).thenReturn(mockInterpreterGroup);
|
||||
spyInterpreterSettingList.add(mockInterpreterSetting);
|
||||
when(mockNote.getId()).thenReturn("any_id");
|
||||
when(mockInterpreterSettingManager.getInterpreterSettings(anyString())).thenReturn(spyInterpreterSettingList);
|
||||
|
||||
doReturn("spy script body").when(spyParagraph).getScriptBody();
|
||||
|
||||
when(mockInterpreter.getFormType()).thenReturn(FormType.NONE);
|
||||
|
||||
ParagraphJobListener mockJobListener = mock(ParagraphJobListener.class);
|
||||
doReturn(mockJobListener).when(spyParagraph).getListener();
|
||||
doNothing().when(mockJobListener).onOutputUpdateAll(Mockito.<Paragraph>any(), Mockito.anyList());
|
||||
|
||||
InterpreterResult mockInterpreterResult = mock(InterpreterResult.class);
|
||||
when(mockInterpreter.interpret(anyString(), Mockito.<InterpreterContext>any())).thenReturn(mockInterpreterResult);
|
||||
when(mockInterpreterResult.code()).thenReturn(Code.SUCCESS);
|
||||
|
||||
|
||||
// Actual test
|
||||
List<InterpreterResultMessage> result1 = Lists.newArrayList();
|
||||
result1.add(new InterpreterResultMessage(Type.TEXT, "result1"));
|
||||
when(mockInterpreterResult.message()).thenReturn(result1);
|
||||
|
||||
AuthenticationInfo user1 = new AuthenticationInfo("user1");
|
||||
spyParagraph.setAuthenticationInfo(user1);
|
||||
spyParagraph.jobRun();
|
||||
Paragraph p1 = spyParagraph.getUserParagraph(user1.getUser());
|
||||
|
||||
List<InterpreterResultMessage> result2 = Lists.newArrayList();
|
||||
result2.add(new InterpreterResultMessage(Type.TEXT, "result2"));
|
||||
when(mockInterpreterResult.message()).thenReturn(result2);
|
||||
|
||||
AuthenticationInfo user2 = new AuthenticationInfo("user2");
|
||||
spyParagraph.setAuthenticationInfo(user2);
|
||||
spyParagraph.jobRun();
|
||||
Paragraph p2 = spyParagraph.getUserParagraph(user2.getUser());
|
||||
|
||||
assertNotEquals(p1.getReturn().toString(), p2.getReturn().toString());
|
||||
|
||||
assertEquals(p1, spyParagraph.getUserParagraph(user1.getUser()));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,12 +78,8 @@ public class GitNotebookRepoTest {
|
|||
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), zeppelinDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_STORAGE.getVarName(), "org.apache.zeppelin.notebook.repo.GitNotebookRepo");
|
||||
|
||||
MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
conf = ZeppelinConfiguration.create();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,18 +35,13 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
|||
import org.apache.zeppelin.dep.DependencyResolver;
|
||||
import org.apache.zeppelin.interpreter.InterpreterFactory;
|
||||
import org.apache.zeppelin.interpreter.InterpreterOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterOutput;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResultMessage;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSettingManager;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter2;
|
||||
import org.apache.zeppelin.notebook.*;
|
||||
import org.apache.zeppelin.scheduler.Job;
|
||||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
import org.apache.zeppelin.scheduler.JobListener;
|
||||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
import org.apache.zeppelin.search.SearchService;
|
||||
import org.apache.zeppelin.search.LuceneSearch;
|
||||
import org.apache.zeppelin.user.AuthenticationInfo;
|
||||
import org.apache.zeppelin.user.Credentials;
|
||||
import org.junit.After;
|
||||
|
|
@ -90,7 +85,6 @@ public class NotebookRepoSyncTest implements JobListenerFactory {
|
|||
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), mainZepDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), mainNotebookDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_STORAGE.getVarName(), "org.apache.zeppelin.notebook.repo.VFSNotebookRepo,org.apache.zeppelin.notebook.repo.mock.VFSNotebookRepoMock");
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_ONE_WAY_SYNC.getVarName(), "false");
|
||||
LOG.info("main Note dir : " + mainNotePath);
|
||||
|
|
@ -99,9 +93,6 @@ public class NotebookRepoSyncTest implements JobListenerFactory {
|
|||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
|
||||
MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
depResolver = new DependencyResolver(mainZepDir.getAbsolutePath() + "/local-repo");
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
|
|
|
|||
|
|
@ -22,15 +22,22 @@ import static org.mockito.Mockito.mock;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
||||
import org.apache.zeppelin.dep.Dependency;
|
||||
import org.apache.zeppelin.dep.DependencyResolver;
|
||||
import org.apache.zeppelin.interpreter.InterpreterFactory;
|
||||
import org.apache.zeppelin.interpreter.InterpreterInfo;
|
||||
import org.apache.zeppelin.interpreter.InterpreterOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterProperty;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSettingManager;
|
||||
import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
|
||||
import org.apache.zeppelin.notebook.JobListenerFactory;
|
||||
|
|
@ -76,19 +83,22 @@ public class VFSNotebookRepoTest implements JobListenerFactory {
|
|||
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), mainZepDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), mainNotebookDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_STORAGE.getVarName(), "org.apache.zeppelin.notebook.repo.VFSNotebookRepo");
|
||||
conf = ZeppelinConfiguration.create();
|
||||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
|
||||
MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1");
|
||||
|
||||
this.schedulerFactory = new SchedulerFactory();
|
||||
depResolver = new DependencyResolver(mainZepDir.getAbsolutePath() + "/local-repo");
|
||||
interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true));
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager);
|
||||
|
||||
ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>();
|
||||
interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>()));
|
||||
interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(),
|
||||
Maps.<String, InterpreterProperty>newHashMap(), "mock1", null);
|
||||
interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(), new Properties());
|
||||
|
||||
SearchService search = mock(SearchService.class);
|
||||
notebookRepo = new VFSNotebookRepo(conf);
|
||||
notebookAuthorization = NotebookAuthorization.init(conf);
|
||||
|
|
|
|||
Loading…
Reference in a new issue