Repository: metron Updated Branches: refs/heads/master 55a737921 -> 2dd01b177
http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellOptionsValidatorTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellOptionsValidatorTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellOptionsValidatorTest.java new file mode 100644 index 0000000..8ca0959 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellOptionsValidatorTest.java @@ -0,0 +1,184 @@ +/* + * + * 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.metron.stellar.common.shell.cli; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.PosixParser; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +public class StellarShellOptionsValidatorTest { + + @Test + public void validateOptions() throws Exception { + String[] validZHostArg = new String[]{"-z", "localhost:8888"}; + String[] validZHostArgNoPort = new String[]{"-z", "localhost"}; + String[] validZIPArgNoPort = new String[]{"-z", "10.10.10.3"}; + String[] validZHostArgList = new String[]{"-z", "localhost:8888,localhost:2181,localhost"}; + String[] validZIPArg = new String[]{"-z", "10.10.10.3:9999"}; + String[] invalidZNameArg = new String[]{"-z", "!!!@!!@!:8882"}; + String[] invalidZIPArg = new String[]{"-z", "11111.22222.10.3:3332"}; + String[] invalidZMissingNameArg = new String[]{"-z", ":8882"}; + String[] invalidZZeroPortArg = new String[]{"-z", "youtube.com:0"}; + String[] invalidZHugePortArg = new String[]{"-z", "youtube.com:75565"}; + + + String existingFileName = "./target/existsFile"; + String nonExistentFile = "./target/doesNotExist"; + + String[] validVFileArg = new String[]{"-v", existingFileName}; + String[] validIrcFileArg = new String[]{"-irc", existingFileName}; + String[] validPFileArg = new String[]{"-p", existingFileName}; + String[] invalidVFileArg = new String[]{"-v", nonExistentFile}; + String[] invalidIrcFileArg = new String[]{"-irc", nonExistentFile}; + String[] invalidPFileArg = new String[]{"-p", nonExistentFile}; + + File existingFile = new File(existingFileName); + if (!existingFile.exists()) { + existingFile.createNewFile(); + } + Options options = new Options(); + options.addOption("z", "zookeeper", true, "Zookeeper URL"); + options.addOption("v", "variables", true, "File containing a JSON Map of variables"); + options.addOption("irc", "inputrc", true, + "File containing the inputrc if not the default ~/.inputrc"); + options.addOption("na", "no_ansi", false, "Make the input prompt not use ANSI colors."); + options.addOption("h", "help", false, "Print help"); + options.addOption("p", "properties", true, "File containing Stellar properties"); + + CommandLineParser parser = new PosixParser(); + + // these should work + CommandLine commandLine = parser.parse(options, validZHostArg); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validZIPArg); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validVFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validIrcFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validPFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validZHostArgNoPort); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validZHostArgList); + StellarShellOptionsValidator.validateOptions(commandLine); + + commandLine = parser.parse(options, validZIPArgNoPort); + StellarShellOptionsValidator.validateOptions(commandLine); + // these should not + + boolean thrown = false; + + + try { + commandLine = parser.parse(options, invalidZNameArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for providing invalid host name ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidZIPArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for providing invalid ip address ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidZMissingNameArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for only providing port ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidZZeroPortArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for 0 port ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidZHugePortArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for port out of range ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidVFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for passing non-existant file to -v ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidVFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for passing non-existant file to -v ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidIrcFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for passing non-existant file to -irc ", thrown); + thrown = false; + + try { + commandLine = parser.parse(options, invalidPFileArg); + StellarShellOptionsValidator.validateOptions(commandLine); + } catch (IllegalArgumentException e) { + thrown = true; + } + Assert.assertTrue("Did not catch failure for passing non-existant file to -p ", thrown); + thrown = false; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellTest.java new file mode 100644 index 0000000..ef2475b --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/cli/StellarShellTest.java @@ -0,0 +1,199 @@ +/* + * 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.metron.stellar.common.shell.cli; + +import com.google.common.collect.Iterables; +import org.jboss.aesh.complete.CompleteOperation; +import org.jboss.aesh.console.AeshContext; +import org.jboss.aesh.console.ConsoleOperation; +import org.jboss.aesh.console.operator.ControlOperator; +import org.jboss.aesh.console.settings.DefaultAeshContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +/** + * Tests the StellarShell class. + */ +public class StellarShellTest { + + private StellarShell stellarShell; + private ByteArrayOutputStream out; + private ByteArrayOutputStream err; + + @Before + public void setup() throws Exception { + + out = new ByteArrayOutputStream(); + err = new ByteArrayOutputStream(); + + // setup streams so that we can capture stdout + System.setOut(new PrintStream(out)); + System.setErr(new PrintStream(err)); + + String[] args = new String[0]; + stellarShell = new StellarShell(args); + } + + @After + public void cleanUp() { + System.setOut(null); + System.setErr(null); + } + + /** + * @return The data written to stdout during the test (with newlines stripped out to simplify comparisons.) + */ + private String stdout() { + return out.toString().replace(System.lineSeparator(), ""); + } + + /** + * @return The data written to stdout during the test. + */ + private String stdoutWithNewlines() { + return out.toString(); + } + + + /** + * @return The data written to stderr during the test. + */ + private String stderr() { + return err.toString().replace(System.lineSeparator(), ""); + } + + /** + * @param buffer + * @return A ConsoleOperation that that StellarShell uses to drive input. + */ + private ConsoleOperation createOp(String buffer) { + return new ConsoleOperation(ControlOperator.APPEND_OUT, buffer); + } + + @Test + public void testExecuteStellar() throws Exception { + stellarShell.execute(createOp("2 + 2")); + assertEquals("4", stdout()); + } + + /** + * Ensure that Stellar lists are displayed correctly in the REPL. + */ + @Test + public void testExecuteWithStellarList() throws Exception { + stellarShell.execute(createOp("[1,2,3,4,5]")); + assertEquals("[1, 2, 3, 4, 5]", stdout()); + } + + /** + * Ensure that Stellar maps are displayed correctly in the REPL. + */ + @Test + public void testExecuteWithStellarMap() throws Exception { + stellarShell.execute(createOp("{ 'foo':2, 'key':'val' }")); + assertEquals("{foo=2, key=val}", stdout()); + } + + /** + * Ensure that 'bad' Stellar code is handled correctly by the REPL. + */ + @Test + public void testExecuteBadStellar() throws Exception { + stellarShell.execute(createOp("2 + ")); + final String expected = "[!] Unable to parse: 2 + "; + assertTrue(stdout().startsWith(expected)); + } + + /** + * The REPL should handle if no value is returned. Some Stellar expressions + * will result in no value. + */ + @Test + public void testExecuteNoop() throws Exception { + stellarShell.execute(createOp("x")); + assertEquals("", stdout()); + } + + /** + * The REPL should handle if the user chooses to quit. + */ + @Test + public void testQuit() throws Exception { + stellarShell.execute(createOp("quit")); + + // the console should not be running + assertFalse(stellarShell.getConsole().isRunning()); + } + + /** + * The REPL should handle if the user chooses to quit. + */ + @Test + public void testStart() throws Exception { + + StellarShell.main(new String[0]); + + // we should see the welcome prompt + assertTrue(stdoutWithNewlines().contains(StellarShell.WELCOME)); + } + + /** + * The REPL should support auto-completion. + */ + @Test + public void testAutoComplete() throws Exception { + + // the user's input that needs auto-completed + final String buffer = "TO_"; + + // the cursor is at the end of the buffer + int cursor = buffer.length(); + + // ask the shell to auto-complete + AeshContext context = new DefaultAeshContext(); + CompleteOperation op = new CompleteOperation(context, buffer, cursor); + stellarShell.complete(op); + + // we should have some auto-complete candidates + List<String> candidates = op.getFormattedCompletionCandidates(); + assertTrue(candidates.size() > 0); + + // validate each candidate + for(String candidate: candidates) { + String completion = buffer + candidate; + + // the auto-complete should include an open paren + assertEquals("(", completion.substring(completion.length() - 1)); + + // the candidate should be a valid, defined function + String function = completion.substring(0, completion.length() - 1); + Iterable<String> allFunctions = stellarShell.getExecutor().getFunctionResolver().getFunctions(); + String definedFunction = Iterables.find(allFunctions, (fn) -> fn.equals(function)); + assertEquals(function, definedFunction); + } + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/AssignmentCommandTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/AssignmentCommandTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/AssignmentCommandTest.java new file mode 100644 index 0000000..899effb --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/AssignmentCommandTest.java @@ -0,0 +1,212 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + + +/** + * Tests the AssignmentCommand class. + */ +public class AssignmentCommandTest { + + AssignmentCommand command; + StellarShellExecutor executor; + + @Before + public void setup() throws Exception { + command = new AssignmentCommand(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals(":=", command.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "x := 2 + 2", + " x := 2 + 2 ", + " x := 2", + " x := " + ); + for(String in : inputs) { + assertTrue("failed: " + in, command.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "2+2", + " %define x := 2", + "x" + ); + for(String in : inputs) { + assertFalse("failed: " + in, command.getMatcher().apply(in)); + } + } + + @Test + public void testAssignment() { + StellarResult result = command.execute("x := 2 + 2", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + assertEquals(4, result.getValue().get()); + + // validate assignment + assertEquals(4, executor.getState().get("x").getResult()); + } + + @Test + public void testAssignments() { + + // execute a series of assignments + assertTrue(command.execute("x := 2 + 2", executor).isSuccess()); + assertTrue(command.execute("y := 2 + x", executor).isSuccess()); + assertTrue(command.execute("z := x + y", executor).isSuccess()); + + // validate assignment + assertEquals(4, executor.getState().get("x").getResult()); + assertEquals(6, executor.getState().get("y").getResult()); + assertEquals(10, executor.getState().get("z").getResult()); + } + + @Test + public void testReassignment() { + + // execute a series of assignments + assertTrue(command.execute("x := 2 + 2", executor).isSuccess()); + assertTrue(command.execute("x := 5 + 5", executor).isSuccess()); + + // validate assignment + assertEquals(10, executor.getState().get("x").getResult()); + } + + @Test + public void testAssignmentOfEmptyVar() { + + // z is not defined + StellarResult result = command.execute("x := z", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.isValueNull()); + assertFalse(result.getValue().isPresent()); + + // the value of x is null + assertNull(executor.getState().get("x").getResult()); + } + + @Test + public void testBadAssignmentExpr() { + StellarResult result = command.execute("x := 2 + ", executor); + + // validate the result + assertTrue(result.isError()); + assertTrue(result.getException().isPresent()); + + // no assignment should have happened + assertFalse(executor.getState().containsKey("x")); + } + + @Test + public void testAssignNull() { + StellarResult result = command.execute("x := NULL", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.isValueNull()); + + // validate assignment + assertNull(executor.getState().get("x").getResult()); + } + + /** + * Assignment with no expression results in an empty string. Is this + * what we would expect? + */ + @Test + public void testNoAssignmentExpr() { + StellarResult result = command.execute("x := ", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // validate assignment + assertEquals("", executor.getState().get("x").getResult()); + } + + @Test + public void testAssignmentWithVar() { + + // define a variable + executor.assign("x", 10, Optional.empty()); + + // execute the assignment expression + StellarResult result = command.execute("y := x + 2", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + assertEquals(12, result.getValue().get()); + + // validate assignment + assertEquals(10, executor.getState().get("x").getResult()); + assertEquals(12, executor.getState().get("y").getResult()); + } + + @Test + public void testAssignmentWithOddWhitespace() { + + StellarResult result = command.execute(" x := 2 + 2", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + assertEquals(4, result.getValue().get()); + + // validate assignment + assertEquals(4, executor.getState().get("x").getResult()); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/CommentTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/CommentTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/CommentTest.java new file mode 100644 index 0000000..0bd5baa --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/CommentTest.java @@ -0,0 +1,91 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CommentTest { + + Comment magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new Comment(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("#", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "#comment", + " # comment ", + " #comment" + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " define ", + "bar" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testComment() { + StellarResult result = magic.execute("# this is a comment ", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + assertEquals("", result.getValue().get()); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/DocCommandTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/DocCommandTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/DocCommandTest.java new file mode 100644 index 0000000..c3a39c4 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/DocCommandTest.java @@ -0,0 +1,86 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.dsl.functions.StringFunctions; +import org.apache.metron.stellar.dsl.functions.resolver.SimpleFunctionResolver; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertTrue; + +public class DocCommandTest { + + DocCommand command; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the command + command = new DocCommand(); + + // setup a function resolver - only 3 functions have been defined + SimpleFunctionResolver functionResolver = new SimpleFunctionResolver() + .withClass(StringFunctions.ToString.class) + .withClass(StringFunctions.ToLower.class) + .withClass(StringFunctions.ToUpper.class); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(functionResolver, props, Optional.empty()); + executor.init(); + } + + @Test + public void testWithFunction() { + StellarResult result = command.execute("?TO_STRING", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // validate that we have some sort of doc string + assertTrue(result.getValue().toString().length() > 0); + } + + @Test + public void testFunctionNotDefined() { + StellarResult result = command.execute("?INVALID", executor); + + // validate the result + assertTrue(result.isError()); + assertTrue(result.getException().isPresent()); + } + + @Test + public void testNoFunction() { + StellarResult result = command.execute("?", executor); + + // validate the result + assertTrue(result.isError()); + assertTrue(result.getException().isPresent()); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicDefineGlobalTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicDefineGlobalTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicDefineGlobalTest.java new file mode 100644 index 0000000..4749103 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicDefineGlobalTest.java @@ -0,0 +1,140 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.common.utils.ConversionUtils; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MagicDefineGlobalTest { + + MagicDefineGlobal magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new MagicDefineGlobal(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("%define", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "%define", + " %define ", + "%define x := 2", + " %define x := 2 " + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " define ", + "bar" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testDefine() { + final int expected = 4; + + { + StellarResult result = magic.execute("%define global := 2 + 2", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + assertEquals(expected, result.getValue().get()); + + // ensure global config was updated + assertTrue(executor.getGlobalConfig().containsKey("global")); + assertEquals(expected, executor.getGlobalConfig().get("global")); + } + // + { + // get all globals + StellarResult result = executor.execute("%globals"); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + String out = ConversionUtils.convert(result.getValue().get(), String.class); + assertEquals("{global=4}", out); + } + } + + @Test + public void testNotAssignmentExpression() { + StellarResult result = magic.execute("%define 2 + 2", executor); + + // validate the result + assertTrue(result.isError()); + assertFalse(result.getValue().isPresent()); + assertTrue(result.getException().isPresent()); + + // the global config should not have changed + assertEquals(0, executor.getGlobalConfig().size()); + } + + @Test + public void testMissingExpression() { + StellarResult result = magic.execute("%define", executor); + + // validate the result + assertTrue(result.isError()); + assertFalse(result.getValue().isPresent()); + assertTrue(result.getException().isPresent()); + + // the global config should not have changed + assertEquals(0, executor.getGlobalConfig().size()); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListFunctionsTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListFunctionsTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListFunctionsTest.java new file mode 100644 index 0000000..204f9a3 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListFunctionsTest.java @@ -0,0 +1,136 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.common.utils.ConversionUtils; +import org.apache.metron.stellar.dsl.functions.StringFunctions; +import org.apache.metron.stellar.dsl.functions.resolver.SimpleFunctionResolver; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MagicListFunctionsTest { + + MagicListFunctions magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new MagicListFunctions(); + + // setup a function resolver - only 3 functions have been defined + SimpleFunctionResolver functionResolver = new SimpleFunctionResolver() + .withClass(StringFunctions.ToString.class) + .withClass(StringFunctions.ToLower.class) + .withClass(StringFunctions.ToUpper.class); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(functionResolver, props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("%functions", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "%functions", + " %functions ", + "%functions FOO", + " %functions FOO " + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " functions ", + "bar", + "%define" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testFunctions() { + StellarResult result = magic.execute("%functions", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // there are 3 functions that should be returned + String value = ConversionUtils.convert(result.getValue().get(), String.class); + String[] functions = value.split(", "); + assertEquals(3, functions.length); + } + + @Test + public void testFunctionsWithMatch() { + StellarResult result = magic.execute("%functions UPPER", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // only 1 function; TO_UPPER should be returned + String value = ConversionUtils.convert(result.getValue().get(), String.class); + String[] functions = value.split(", "); + assertEquals(1, functions.length); + assertEquals("TO_UPPER", functions[0]); + } + + @Test + public void testFunctionsWithNoMatch() { + StellarResult result = magic.execute("%functions NOMATCH", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // no functions should be returned + String value = ConversionUtils.convert(result.getValue().get(), String.class); + String[] functions = value.trim().split(", "); + assertEquals(1, functions.length); + assertEquals("", functions[0]); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListGlobalsTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListGlobalsTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListGlobalsTest.java new file mode 100644 index 0000000..f02bfdb --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListGlobalsTest.java @@ -0,0 +1,113 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.common.utils.ConversionUtils; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MagicListGlobalsTest { + + MagicListGlobals magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new MagicListGlobals(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("%globals", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "%globals", + " %globals ", + "%globals FOO", + " %globals FOO " + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " globals ", + "bar", + "%define" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void test() { + // define some globals + executor.getGlobalConfig().put("x", 2); + + // get all globals + StellarResult result = executor.execute("%globals"); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + String out = ConversionUtils.convert(result.getValue().get(), String.class); + assertEquals("{x=2}", out); + } + + @Test + public void testWithNoGlobals() { + // get all globals + StellarResult result = executor.execute("%globals"); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + String out = ConversionUtils.convert(result.getValue().get(), String.class); + assertEquals("{}", out); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListVariablesTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListVariablesTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListVariablesTest.java new file mode 100644 index 0000000..51ef0ac --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicListVariablesTest.java @@ -0,0 +1,113 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.common.utils.ConversionUtils; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MagicListVariablesTest { + + MagicListVariables magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new MagicListVariables(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("%vars", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "%vars", + " %vars ", + "%vars FOO", + " %vars FOO " + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " vars ", + "bar", + "%define" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void test() { + // define some vars + executor.execute("x := 2 + 2"); + StellarResult result = executor.execute("%vars"); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // validate the list of vars + String vars = ConversionUtils.convert(result.getValue().get(), String.class); + assertEquals("x = 4 via `2 + 2`", vars); + } + + @Test + public void testWithNoVars() { + // there are no vars defined + StellarResult result = executor.execute("%vars"); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // validate the list of vars + String vars = ConversionUtils.convert(result.getValue().get(), String.class); + assertEquals("", vars); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicUndefineGlobalTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicUndefineGlobalTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicUndefineGlobalTest.java new file mode 100644 index 0000000..4db02f4 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/shell/specials/MagicUndefineGlobalTest.java @@ -0,0 +1,117 @@ +/* + * + * 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.metron.stellar.common.shell.specials; + +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MagicUndefineGlobalTest { + + MagicUndefineGlobal magic; + DefaultStellarShellExecutor executor; + + @Before + public void setup() throws Exception { + + // setup the %magic + magic = new MagicUndefineGlobal(); + + // setup the executor + Properties props = new Properties(); + executor = new DefaultStellarShellExecutor(props, Optional.empty()); + executor.init(); + } + + @Test + public void testGetCommand() { + assertEquals("%undefine", magic.getCommand()); + } + + @Test + public void testShouldMatch() { + List<String> inputs = Arrays.asList( + "%undefine", + " %undefine ", + "%undefine FOO", + " %undefine FOO " + ); + for(String in : inputs) { + assertTrue("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testShouldNotMatch() { + List<String> inputs = Arrays.asList( + "foo", + " undefine ", + "bar", + "%define" + ); + for(String in : inputs) { + assertFalse("failed: " + in, magic.getMatcher().apply(in)); + } + } + + @Test + public void testUndefine() { + // define a global + executor.getGlobalConfig().put("global", 22); + assertEquals(1, executor.getGlobalConfig().size()); + + // use the magic to undefine the global variable + StellarResult result = magic.execute("%undefine global", executor); + + // validate the result + assertTrue(result.isSuccess()); + assertTrue(result.getValue().isPresent()); + + // ensure that the global variable does not exist + assertEquals(0, executor.getGlobalConfig().size()); + } + + @Test + public void testWithNoVariable() { + // define a global + executor.getGlobalConfig().put("global", 22); + assertEquals(1, executor.getGlobalConfig().size()); + + // no arg specifying the var to undefine + StellarResult result = magic.execute("%undefine", executor); + + // validate the result + assertTrue(result.isError()); + assertTrue(result.getException().isPresent()); + + // ensure that the global variables were not changed + assertEquals(1, executor.getGlobalConfig().size()); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/README.md ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/README.md b/metron-stellar/stellar-zeppelin/README.md new file mode 100644 index 0000000..1190658 --- /dev/null +++ b/metron-stellar/stellar-zeppelin/README.md @@ -0,0 +1,160 @@ +<!-- +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. +--> + +Stellar Interpreter for Apache Zeppelin +======================================= + +[Apache Zeppelin](https://zeppelin.apache.org/) is a web-based notebook that enables data-driven, interactive data analytics and collaborative documents with SQL, Scala and more. This project provides a means to run the Stellar REPL directly within a Zeppelin Notebook. + +* [Prerequisites](#prerequisites) +* [Installation](#installation) +* [Usage](#usage) + + +Prerequisites +------------- + +* [Apache Zeppelin](https://zeppelin.apache.org/) 0.7.3 + + This is tested with version 0.7.3. Other versions may work, but are not supported. + + +Installation +------------ + +Currently, you need to manually install the Stellar Interpreter in Zeppelin. In the future this step could be automated by the Metron Mpack. + +To install the Stellar Interpreter in your Apache Zeppelin installation, follow these instructions. This is paraphrased from the [Zeppelin docs](https://zeppelin.apache.org/docs/latest/development/writingzeppelininterpreter.html#install-your-interpreter-binary). + +1. Build and install Metron. Metron and its dependencies will be retrieved from your local Maven repository. + + ``` + cd $METRON_HOME + mvn clean install -DskipTests + ``` + +1. If you do not already have Zeppelin installed, [download and unpack Apache Zeppelin](https://zeppelin.apache.org/download.html). The directory in which you unpack Zeppelin will be referred to as `$ZEPPELIN_HOME`. + +1. If Zeppelin was already installed, make sure that it is not running. + +1. Create a settings directory for the Stellar interpreter. + + ``` + mkdir $ZEPPELIN_HOME/interpreter/stellar + cat <<EOF > $ZEPPELIN_HOME/interpreter/stellar/interpreter-setting.json + [ + { + "group": "stellar", + "name": "stellar", + "className": "org.apache.metron.stellar.zeppelin.StellarInterpreter", + "properties": { + } + } + ] + EOF + ``` + +1. Create a Zeppelin Site file (`$ZEPPELIN_HOME/conf/zeppelin-site.xml`). + + ``` + cp $ZEPPELIN_HOME/conf/zeppelin-site.xml.template $ZEPPELIN_HOME/conf/zeppelin-site.xml + ``` + +1. In the Zeppelin site file, add `org.apache.metron.stellar.zeppelin.StellarInterpreter` to the comma-separated list of Zeppelin interpreters under the `zeppelin.interpreters` property. + + The property will likely look-like the following. + ``` + <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.postgresql.PostgreS qlInterpreter,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,org.apache.metron.stellar.zeppelin.StellarInterpreter</value> + <description>Comma separated interpreter configurations. First interpreter become a default</description> + </property> + ``` + +1. Start Zeppelin. + + ``` + $ZEPPELIN_HOME/bin/zeppelin-daemon.sh start + ``` + +1. Navigate to Zeppelin running at [http://localhost:8080/](http://localhost:8080/). + +1. Register the Stellar interpreter in Zeppelin. + + 1. Click on the top-right menu item labelled "Anonymous" then choose "Interpreter" in the drop-down that opens. + +1. Configure the Stellar interpreter. + + 1. Click on '**+ Create**' near the top-right. + + 1. Define the following values. + * **Interpreter Name** = `stellar` + * **Interpreter Group** = `stellar` + + 1. Under **Options**, set the following values. + * The interpreter will be instantiated **Per Note** in **isolated** process. + + 1. Under **Dependencies**, define the following fields, then click the "+" icon. Replace the Metron version as required. + * **Artifact** = `org.apache.metron:stellar-zeppelin:0.4.3` + + 1. Click "Save" + +1. Wait for the intrepreter to start. + + 1. Near the title '**stellar**', will be a status icon. This will indicate that it is downloading the dependencies. + + 1. Once the icon is shown as green, the interpreter is ready to work. + +Usage +----- + +1. Create a new notebook. + + 1. Click on "Notebook" > "Create new note". + + 1. Set the default Interpreter to `stellar`. + + When creating the notebook, if you define `stellar` as the default interpreter, then there is no need to enter `%stellar` at the top of each code block. + + If `stellar` is not the default interpreter, then you must enter `%stellar` at the top of a code block containing Stellar code. + +1. In the first block, add the following Stellar, then click Run. + + ``` + 2 in [2,3,4] + ``` + +1. In the next block, check which functions are available to you. + + ``` + %functions + ``` + + You will **only** 'see' the functions defined within `stellar-common` since that is the only library that we added to the interpreter. + +1. To see how additional functions can be added, go back to the Stellar interpreter configuration and add another dependency as follows. + + ``` + org.apache.metron:metron-statistics:0.4.3 + ``` + + Reload the Stellar interpreter and run `%functions` again. You will see the additional functions defined within the `metron-statistics` project. + +1. Auto-completion is also available for Stellar expressions. + + In another block, type 'TO_' then press the <kbd>CTRL</kbd> + <kbd>PERIOD</kbd> keys. This will trigger the auto-complete mechanism in Stellar and display a list of matching functions or variables. http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/pom.xml ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/pom.xml b/metron-stellar/stellar-zeppelin/pom.xml new file mode 100644 index 0000000..7809342 --- /dev/null +++ b/metron-stellar/stellar-zeppelin/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>metron-stellar</artifactId> + <groupId>org.apache.metron</groupId> + <version>0.4.3</version> + </parent> + <name>stellar-zeppelin</name> + <description>Stellar Interpreter for Apache Zeppelin</description> + <artifactId>stellar-zeppelin</artifactId> + <dependencies> + <dependency> + <groupId>org.apache.zeppelin</groupId> + <artifactId>zeppelin-interpreter</artifactId> + <version>0.7.3</version> + </dependency> + <dependency> + <groupId>org.apache.metron</groupId> + <artifactId>stellar-common</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>${global_mockito_version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>${global_jar_version}</version> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.antlr</groupId> + <artifactId>antlr4-maven-plugin</artifactId> + <version>${global_antlr_version}</version> + <configuration> + <outputDirectory>${basedir}/src/main/java</outputDirectory> + </configuration> + <executions> + <execution> + <goals> + <goal>antlr4</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <descriptor>src/main/assembly/assembly.xml</descriptor> + </configuration> + <executions> + <execution> + <id>make-assembly</id> <!-- this is used for inheritance merges --> + <phase>package</phase> <!-- bind to the packaging phase --> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + <resources> + <resource> + <directory>src/main/resources</directory> + </resource> + </resources> + </build> +</project> http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/src/main/assembly/assembly.xml ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/src/main/assembly/assembly.xml b/metron-stellar/stellar-zeppelin/src/main/assembly/assembly.xml new file mode 100644 index 0000000..090835b --- /dev/null +++ b/metron-stellar/stellar-zeppelin/src/main/assembly/assembly.xml @@ -0,0 +1,30 @@ +<!-- + 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. + --> + +<assembly> + <id>archive</id> + <formats> + <format>tar.gz</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + <fileSets> + <fileSet> + <directory>${project.basedir}/target</directory> + <includes> + <include>${project.artifactId}-${project.version}.jar</include> + </includes> + <outputDirectory>lib</outputDirectory> + <useDefaultExcludes>true</useDefaultExcludes> + </fileSet> + </fileSets> +</assembly> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/src/main/java/org/apache/metron/stellar/zeppelin/StellarInterpreter.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/src/main/java/org/apache/metron/stellar/zeppelin/StellarInterpreter.java b/metron-stellar/stellar-zeppelin/src/main/java/org/apache/metron/stellar/zeppelin/StellarInterpreter.java new file mode 100644 index 0000000..58287dc --- /dev/null +++ b/metron-stellar/stellar-zeppelin/src/main/java/org/apache/metron/stellar/zeppelin/StellarInterpreter.java @@ -0,0 +1,200 @@ +/* + * 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.metron.stellar.zeppelin; + +import static org.apache.zeppelin.interpreter.InterpreterResult.Code.ERROR; +import static org.apache.zeppelin.interpreter.InterpreterResult.Code.SUCCESS; +import static org.apache.zeppelin.interpreter.InterpreterResult.Type.TEXT; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.metron.stellar.common.shell.DefaultStellarAutoCompleter; +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor; +import org.apache.metron.stellar.common.shell.StellarAutoCompleter; +import org.apache.metron.stellar.common.shell.StellarResult; +import org.apache.metron.stellar.common.shell.StellarShellExecutor; +import org.apache.zeppelin.interpreter.Interpreter; +import org.apache.zeppelin.interpreter.InterpreterContext; +import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Zeppelin Interpreter for Stellar. + */ +public class StellarInterpreter extends Interpreter { + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + /** + * Executes the Stellar expressions. + * + * <p>Zeppelin will handle isolation and how the same executor is or is not used across + * multiple notebooks. This is configurable by the user. + * + * <p>See https://zeppelin.apache.org/docs/latest/manual/interpreters.html#interpreter-binding-mode. + */ + private StellarShellExecutor executor; + + /** + * Handles auto-completion for Stellar expressions. + */ + private StellarAutoCompleter autoCompleter; + + public StellarInterpreter(Properties properties) { + super(properties); + this.autoCompleter = new DefaultStellarAutoCompleter(); + } + + @Override + public void open() { + try { + executor = createExecutor(); + + } catch (Exception e) { + LOG.error("Unable to create a StellarShellExecutor", e); + } + } + + @Override + public void close() { + // nothing to do + } + + @Override + public InterpreterResult interpret(final String input, InterpreterContext context) { + InterpreterResult result; + try { + + // execute the input + StellarResult stellarResult = executor.execute(input); + if(stellarResult.isSuccess()) { + + // on success - if no result, use a blank value + Object value = stellarResult.getValue().orElse(""); + String text = value.toString(); + result = new InterpreterResult(SUCCESS, TEXT, text); + + } else if(stellarResult.isError()) { + + // an error occurred + Optional<Throwable> e = stellarResult.getException(); + String message = getErrorMessage(e, input); + result = new InterpreterResult(ERROR, TEXT, message); + + } else { + + // should never happen + throw new IllegalStateException("Unexpected error. result=" + stellarResult); + } + + } catch(Throwable t) { + + // unexpected exception + String message = getErrorMessage(Optional.of(t), input); + result = new InterpreterResult(ERROR, TEXT, message); + } + + return result; + } + + @Override + public void cancel(InterpreterContext context) { + // there is no way to cancel the execution of a Stellar expression + } + + @Override + public FormType getFormType() { + return FormType.SIMPLE; + } + + @Override + public int getProgress(InterpreterContext context) { + // unable to provide progress + return 0; + } + + @Override + public List<InterpreterCompletion> completion(String buf, int cursor) { + + // use the autoCompleter to return a list of completes to Zeppelin + List<InterpreterCompletion> completes = new ArrayList<>(); + for(String candidate : autoCompleter.autoComplete(buf)) { + completes.add(new InterpreterCompletion(candidate, candidate)); + } + + return completes; + } + + /** + * Generates an error message that is shown to the user. + * + * @param e An optional exception that occurred. + * @param input The user input that led to the error condition. + * @return An error message for the user. + */ + private String getErrorMessage(Optional<Throwable> e, String input) { + String message; + if(e.isPresent()) { + + // base the error message on the exception + String error = ExceptionUtils.getRootCauseMessage(e.get()); + String trace = ExceptionUtils.getStackTrace(e.get()); + message = error + System.lineSeparator() + trace; + + } else { + // no exception provided; create generic error message + message = "Invalid expression: " + input; + } + + return message; + } + + /** + * Create an executor that will run the Stellar code for the Zeppelin Notebook. + * @return The stellar executor. + */ + private StellarShellExecutor createExecutor() throws Exception { + + Properties props = getProperty(); + StellarShellExecutor executor = new DefaultStellarShellExecutor(props, Optional.empty()); + + // register the auto-completer to be notified + executor.addSpecialListener((magic) -> autoCompleter.addCandidateFunction(magic.getCommand())); + executor.addFunctionListener((fn) -> autoCompleter.addCandidateFunction(fn.getName())); + executor.addVariableListener((name, val) -> autoCompleter.addCandidateVariable(name)); + + executor.init(); + return executor; + } + + /** + * Returns the executor used to execute Stellar expressions. + * @return The executor of Stellar expressions. + */ + public StellarShellExecutor getExecutor() { + return executor; + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/src/main/resources/interpreter-setting.json ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/src/main/resources/interpreter-setting.json b/metron-stellar/stellar-zeppelin/src/main/resources/interpreter-setting.json new file mode 100644 index 0000000..cbd974a --- /dev/null +++ b/metron-stellar/stellar-zeppelin/src/main/resources/interpreter-setting.json @@ -0,0 +1,12 @@ +[ + { + "group": "stellar", + "name": "stellar", + "className": "org.apache.metron.stellar.zeppelin.StellarInterpreter", + "properties": { + }, + "editor": { + "completionKey": "TAB" + } + } +] http://git-wip-us.apache.org/repos/asf/metron/blob/2dd01b17/metron-stellar/stellar-zeppelin/src/test/java/org/apache/metron/stellar/zeppelin/StellarInterpreterTest.java ---------------------------------------------------------------------- diff --git a/metron-stellar/stellar-zeppelin/src/test/java/org/apache/metron/stellar/zeppelin/StellarInterpreterTest.java b/metron-stellar/stellar-zeppelin/src/test/java/org/apache/metron/stellar/zeppelin/StellarInterpreterTest.java new file mode 100644 index 0000000..363938e --- /dev/null +++ b/metron-stellar/stellar-zeppelin/src/test/java/org/apache/metron/stellar/zeppelin/StellarInterpreterTest.java @@ -0,0 +1,192 @@ +/* + * 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.metron.stellar.zeppelin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.Iterables; +import org.apache.commons.lang3.StringUtils; +import org.apache.zeppelin.interpreter.InterpreterContext; +import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.InterpreterResultMessage; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.Properties; + +/** + * Tests the StellarInterpreter. + */ +public class StellarInterpreterTest { + + private StellarInterpreter interpreter; + private InterpreterContext context; + + @Before + public void setup() { + Properties props = new Properties(); + interpreter = new StellarInterpreter(props); + interpreter.open(); + + context = mock(InterpreterContext.class); + } + + /** + * Ensure that we can run Stellar code in the interpreter. + */ + @Test + public void testExecuteStellar() { + InterpreterResult result = interpreter.interpret("2 + 2", context); + + // validate the result + assertEquals(InterpreterResult.Code.SUCCESS, result.code()); + assertEquals(1, result.message().size()); + + // validate the message + InterpreterResultMessage message = result.message().get(0); + assertEquals("4", message.getData()); + assertEquals(InterpreterResult.Type.TEXT, message.getType()); + } + + /** + * Ensure that Stellar lists are displayed correctly in Zeppelin. + */ + @Test + public void testExecuteWithStellarList() { + final String expected = "[1, 2, 3, 4, 5]"; + InterpreterResult result = interpreter.interpret("[1,2,3,4,5]", context); + + // validate the result + assertEquals(InterpreterResult.Code.SUCCESS, result.code()); + assertEquals(1, result.message().size()); + + // validate the message + InterpreterResultMessage message = result.message().get(0); + assertEquals(expected, message.getData()); + assertEquals(InterpreterResult.Type.TEXT, message.getType()); + } + + /** + * Ensure that Stellar maps are displayed correctly in Zeppelin. + */ + @Test + public void testExecuteWithStellarMap() { + final String expected = "{foo=2, key=val}"; + InterpreterResult result = interpreter.interpret("{ 'foo':2, 'key':'val' }", context); + + // validate the result + assertEquals(InterpreterResult.Code.SUCCESS, result.code()); + assertEquals(1, result.message().size()); + + // validate the message + InterpreterResultMessage message = result.message().get(0); + assertEquals(expected, message.getData()); + assertEquals(InterpreterResult.Type.TEXT, message.getType()); + } + + /** + * Ensure that 'bad' Stellar code is handled correctly by the interpreter. + */ + @Test + public void testExecuteBadStellar() { + InterpreterResult result = interpreter.interpret("2 + ", context); + + // validate the result + assertEquals(InterpreterResult.Code.ERROR, result.code()); + assertEquals(1, result.message().size()); + + // validate the message + InterpreterResultMessage message = result.message().get(0); + assertTrue(message.getData().length() > 0); + assertEquals(InterpreterResult.Type.TEXT, message.getType()); + } + + /** + * The interpreter should handle if no value is returned. Some Stellar expressions + * will result in no value. + */ + @Test + public void testExecuteNoop() { + + // x is undefined and will have no result + InterpreterResult result = interpreter.interpret("x", context); + + // validate the result + assertEquals(InterpreterResult.Code.SUCCESS, result.code()); + assertEquals(1, result.message().size()); + + // validate the message + InterpreterResultMessage message = result.message().get(0); + assertEquals(0, message.getData().length()); + assertEquals(InterpreterResult.Type.TEXT, message.getType()); + } + + /** + * The interpreter should support auto-completion. + */ + @Test + public void testAutoCompletion() { + + // the user's input that needs auto-completed + final String buffer = "TO_"; + + // the cursor is at the end of the buffer + int cursor = buffer.length(); + + List<InterpreterCompletion> completions = interpreter.completion(buffer, cursor); + + // expect some completions to be offered + assertTrue(completions.size() > 0); + + for(InterpreterCompletion iCompletion: completions) { + String completion = iCompletion.getValue(); + + // the auto-complete should include an open paren + assertEquals("(", completion.substring(completion.length() - 1)); + + // the candidate should be a valid, defined function + String function = completion.substring(0, completion.length() - 1); + Iterable<String> allFunctions = interpreter.getExecutor().getFunctionResolver().getFunctions(); + String definedFunction = Iterables.find(allFunctions, (fn) -> StringUtils.equals(fn, function)); + assertEquals(function, definedFunction); + } + } + + /** + * What happens when we have nothing useful to auto-complete? + */ + @Test + public void testAutoCompletionWithNoCompletions() { + + // the user's input that needs auto-completed + final String buffer = "NOTHING_AUTOCOMPLETES_THIS_"; + + // the cursor is at the end of the buffer + int cursor = buffer.length(); + + // perform auto-completion + List<InterpreterCompletion> completions = interpreter.completion(buffer, cursor); + + // expect no completions + assertEquals(0, completions.size()); + } +}
