Repository: incubator-zeppelin
Updated Branches:
refs/heads/master 566ffd0b9 -> 140adb8d3
[ZEPPELIN-695] Add AngularJS z.runParagraph()
### What is this PR for?
Add AngularJS `z.runParagraph()` method
corneadoug
On the client-side, inside the scope of a paragraph, we cannot access to the
list of all existing paragraphs for the current note. In my previous
implementation, I was using the following trick:
```
var paragraphDiv = angular.element('#' + paragraphId +
'_paragraphColumn_main[ng-controller="ParagraphCtrl"]');
var paragraph = paragraphDiv.scope().paragraph;
```
This is **dirty** and can be broken if we change the CSS style tomorrow for
paragraph.
For the current implementation, what I did is to **inject** a reference to the
`note` object into the `$scope` of paragraph so that we can access the current
list of paragraph **programmatically**:
```javascript
$scope.$broadcast('updateParagraph', {
note: $scope.note, // pass the note object to paragraph scope
paragraph: note.paragraphs[index]});
```
```html
<div id="{{currentParagraph.id}}_paragraphColumn_main"
ng-repeat="currentParagraph in note.paragraphs"
ng-controller="ParagraphCtrl"
ng-Init="init(currentParagraph, note)"
```
_This is a sub-task of epic **[ZEPPELIN-635]**_
### What type of PR is it?
[Improvement### Todos
* [ ] - Code Review
* [ ] - Simple Test
### Is there a relevant Jira issue?
**[ZEPPELIN-695]**
### How should this be tested?
* `git fetch origin pull/742/head:AngularJSRunParagraph`
* `git checkout AngularJSRunParagraph`
* `mvn clean package -DskipTests`
* `bin/zeppelin-daemon.sh restart`
* Create a new note
* In the first paragraph, put the following code
```html
%angular
<form class="form-inline">
<div class="form-group">
<label for="paragraphId">Paragraph Id: </label>
<input type="text" class="form-control" id="paragraphId"
placeholder="Paragraph Id ..." ng-model="paragraph"></input>
</div>
<button type="submit" class="btn btn-primary"
ng-click="z.runParagraph(paragraph)"> Run Paragraph</button>
</form>
```
* Create a second paragraph with the following code:
```scala
println("Date "+new java.util.Date().toString)
```
* Retrieve the paragraph id of the 2nd paragraph
* In the first paragraph, put the paragraph id in the input text and click on
the **Run Paragraph** button, it should trigger execution of the second
paragraph
### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? --> **No**
* Is there breaking changes for older versions? --> **No**
* Does this needs documentation? --> **Yes**
[ZEPPELIN-635]: https://issues.apache.org/jira/browse/ZEPPELIN-635
[ZEPPELIN-695]: https://issues.apache.org/jira/browse/ZEPPELIN-695
Author: DuyHai DOAN <[email protected]>
Closes #742 from doanduyhai/ZEPPELIN-695 and squashes the following commits:
cf0e6e4 [DuyHai DOAN] [ZEPPELIN-695] Add AngularJS z.runParagraph()
Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo
Commit:
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/140adb8d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/140adb8d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/140adb8d
Branch: refs/heads/master
Commit: 140adb8d3939aef850b646bbecbc7b63e0b99f94
Parents: 566ffd0
Author: DuyHai DOAN <[email protected]>
Authored: Sat Mar 19 14:33:41 2016 +0100
Committer: Lee moon soo <[email protected]>
Committed: Sat Mar 26 16:19:00 2016 -0700
----------------------------------------------------------------------
.../apache/zeppelin/integration/ZeppelinIT.java | 72 ++++++++++++++++++--
zeppelin-web/src/app/home/home.html | 2 +-
.../src/app/notebook/notebook.controller.js | 8 ++-
zeppelin-web/src/app/notebook/notebook.html | 4 +-
.../notebook/paragraph/paragraph.controller.js | 19 +++++-
5 files changed, 95 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/140adb8d/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
----------------------------------------------------------------------
diff --git
a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
index d7f3b49..bad8b84 100644
---
a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
+++
b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
@@ -17,13 +17,11 @@
package org.apache.zeppelin.integration;
+import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.AbstractZeppelinIT;
import org.apache.zeppelin.WebDriverManager;
import org.hamcrest.CoreMatchers;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.*;
import org.junit.rules.ErrorCollector;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
@@ -31,7 +29,9 @@ import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
/**
* Test Zeppelin with web browser.
@@ -246,4 +246,68 @@ public class ZeppelinIT extends AbstractZeppelinIT {
handleException("Exception in ZeppelinIT while
testSparkInterpreterDependencyLoading ", e);
}
}
+
+ @Test
+ public void testAngularRunParagraph() throws Exception {
+ if (!endToEndTestEnabled()) {
+ return;
+ }
+
+ try {
+ createNewNote();
+
+ // wait for first paragraph's " READY " status text
+ waitForParagraph(1, "READY");
+
+ // Create 1st paragraph
+ setTextOfParagraph(1,
+ "%angular <div id=\\'angularRunParagraph\\'>Run second
paragraph</div>");
+ runParagraph(1);
+ waitForParagraph(1, "FINISHED");
+ waitForText("Run second paragraph", By.xpath(
+ getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
+
+ // Create 2nd paragraph
+ setTextOfParagraph(2, "%sh echo TEST");
+ runParagraph(2);
+ waitForParagraph(2, "FINISHED");
+
+ // Get 2nd paragraph id
+ final String secondParagraphId =
driver.findElement(By.xpath(getParagraphXPath(2)
+ + "//div[@class=\"control
ng-scope\"]//ul[@class=\"dropdown-menu\"]/li[1]"))
+ .getAttribute("textContent");
+
+ assertTrue("Cannot find paragraph id for the 2nd paragraph",
isNotBlank(secondParagraphId));
+
+ // Update first paragraph to call z.runParagraph() with 2nd paragraph id
+ setTextOfParagraph(1,
+ "%angular <div id=\\'angularRunParagraph\\'
ng-click=\\'z.runParagraph(\""
+ + secondParagraphId.trim()
+ + "\")\\'>Run second paragraph</div>");
+ runParagraph(1);
+ waitForParagraph(1, "FINISHED");
+
+ // Set new text value for 2nd paragraph
+ setTextOfParagraph(2, "%sh echo NEW_VALUE");
+
+ // Click on 1 paragraph to trigger z.runParagraph() function
+ driver.findElement(By.xpath(
+ getParagraphXPath(1) +
"//div[@id=\"angularRunParagraph\"]")).click();
+
+ waitForParagraph(2, "FINISHED");
+
+ // Check that 2nd paragraph has been executed
+ waitForText("NEW_VALUE", By.xpath(
+ getParagraphXPath(2) + "//div[contains(@id,\"_text\") and
@class=\"text\"]"));
+
+ //delete created notebook for cleanup.
+ deleteTestNotebook(driver);
+ sleep(1000, true);
+
+ LOG.info("testAngularRunParagraph Test executed");
+ } catch (Exception e) {
+ handleException("Exception in ZeppelinIT while testAngularRunParagraph",
e);
+ }
+
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/140adb8d/zeppelin-web/src/app/home/home.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/home/home.html
b/zeppelin-web/src/app/home/home.html
index 439dfa6..5eff5e0 100644
--- a/zeppelin-web/src/app/home/home.html
+++ b/zeppelin-web/src/app/home/home.html
@@ -72,7 +72,7 @@ limitations under the License.
<div ng-show="home.notebookHome"
id="{{currentParagraph.id}}_paragraphColumn_main"
ng-repeat="currentParagraph in home.note.paragraphs"
ng-controller="ParagraphCtrl"
- ng-Init="init(currentParagraph)"
+ ng-Init="init(currentParagraph, home.note)"
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
class="paragraph-col">
<div id="{{currentParagraph.id}}_paragraphColumn"
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/140adb8d/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js
b/zeppelin-web/src/app/notebook/notebook.controller.js
index 194cd77..fb1120e 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -488,7 +488,9 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
paragraphToBeFocused = note.paragraphs[index].id;
break;
}
- $scope.$broadcast('updateParagraph', {paragraph:
note.paragraphs[index]});
+ $scope.$broadcast('updateParagraph', {
+ note: $scope.note, // pass the note object to paragraph scope
+ paragraph: note.paragraphs[index]});
}
}
@@ -497,7 +499,9 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
for (var idx in newParagraphIds) {
var newEntry = note.paragraphs[idx];
if (oldParagraphIds[idx] === newParagraphIds[idx]) {
- $scope.$broadcast('updateParagraph', {paragraph: newEntry});
+ $scope.$broadcast('updateParagraph', {
+ note: $scope.note, // pass the note object to paragraph scope
+ paragraph: newEntry});
} else {
// move paragraph
var oldIdx = oldParagraphIds.indexOf(newParagraphIds[idx]);
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/140adb8d/zeppelin-web/src/app/notebook/notebook.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.html
b/zeppelin-web/src/app/notebook/notebook.html
index f2e2bb4..d4b72bc 100644
--- a/zeppelin-web/src/app/notebook/notebook.html
+++ b/zeppelin-web/src/app/notebook/notebook.html
@@ -84,11 +84,11 @@ limitations under the License.
<div class="note-jump"></div>
- <!-- Include the paragraphs according to the note -->
+ <!-- Include the paragraphs according to the note, pass the note to init
function -->
<div id="{{currentParagraph.id}}_paragraphColumn_main"
ng-repeat="currentParagraph in note.paragraphs"
ng-controller="ParagraphCtrl"
- ng-Init="init(currentParagraph)"
+ ng-Init="init(currentParagraph, note)"
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
class="paragraph-col">
<div class="new-paragraph" ng-click="insertNew('above')" ng-hide="viewOnly
|| asIframe">
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/140adb8d/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index a6489fd..bb9bc89 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -18,6 +18,7 @@ angular.module('zeppelinWebApp')
.controller('ParagraphCtrl', function($scope,$rootScope, $route, $window,
$element, $routeParams, $location,
$timeout, $compile, websocketMsgSrv) {
var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
+ $scope.parentNote = null;
$scope.paragraph = null;
$scope.originalText = '';
$scope.editor = null;
@@ -28,6 +29,20 @@ angular.module('zeppelinWebApp')
$scope.compiledScope = paragraphScope;
paragraphScope.z = {
+ // z.runParagraph('20150213-231621_168813393')
+ runParagraph: function(paragraphId) {
+ if (paragraphId) {
+ var filtered = $scope.parentNote.paragraphs.filter(function(x) {
+ return x.id === paragraphId;});
+ if (filtered.length === 1) {
+ var paragraph = filtered[0];
+ websocketMsgSrv.runParagraph(paragraph.id, paragraph.title,
paragraph.text,
+ paragraph.config, paragraph.settings.params);
+ } else {
+ // Error message here
+ }
+ }
+ },
// Example: z.angularBind('my_var', 'Test Value',
'20150213-231621_168813393')
angularBind: function(varName, value, paragraphId) {
@@ -36,6 +51,7 @@ angular.module('zeppelinWebApp')
websocketMsgSrv.clientBindAngularObject($routeParams.noteId, varName,
value, paragraphId);
}
},
+
// Example: z.angularUnBind('my_var', '20150213-231621_168813393')
angularUnbind: function(varName, paragraphId) {
// Only push to server if paragraphId is defined
@@ -55,8 +71,9 @@ angular.module('zeppelinWebApp')
};
// Controller init
- $scope.init = function(newParagraph) {
+ $scope.init = function(newParagraph, note) {
$scope.paragraph = newParagraph;
+ $scope.parentNote = note;
$scope.originalText = angular.copy(newParagraph.text);
$scope.chart = {};
$scope.colWidthOption = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];