http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java ---------------------------------------------------------------------- diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java new file mode 100644 index 0000000..afca9de --- /dev/null +++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java @@ -0,0 +1,670 @@ +/* + * 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.cassandra; + +import static com.datastax.driver.core.ProtocolOptions.DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS; +import static com.google.common.collect.FluentIterable.from; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_CLUSTER_NAME; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_COMPRESSION_PROTOCOL; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_CREDENTIALS_PASSWORD; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_CREDENTIALS_USERNAME; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_HOSTS; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_PORT; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.CASSANDRA_PROTOCOL_VERSION; +import static org.apache.zeppelin.cassandra.CassandraInterpreter.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.when; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import info.archinnov.achilles.junit.AchillesResource; +import info.archinnov.achilles.junit.AchillesResourceBuilder; +import org.apache.zeppelin.interpreter.InterpreterContext; +import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import scala.io.Source; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Properties; + +@RunWith(MockitoJUnitRunner.class) +public class CassandraInterpreterTest { + + private static final String ARTISTS_TABLE = "zeppelin.artists"; + + @ClassRule + public static AchillesResource resource = AchillesResourceBuilder + .noEntityPackages() + .withKeyspaceName("zeppelin") + .withScript("prepare_schema.cql") + .withScript("prepare_data.cql") + .build(); + + private static Session session = resource.getNativeSession(); + + private static CassandraInterpreter interpreter; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private InterpreterContext intrContext; + + @BeforeClass + public static void setUp() { + Properties properties = new Properties(); + final Cluster cluster = resource.getNativeSession().getCluster(); + properties.setProperty(CASSANDRA_CLUSTER_NAME, cluster.getClusterName()); + properties.setProperty(CASSANDRA_COMPRESSION_PROTOCOL, "NONE"); + properties.setProperty(CASSANDRA_CREDENTIALS_USERNAME, "none"); + properties.setProperty(CASSANDRA_CREDENTIALS_PASSWORD, "none"); + + properties.setProperty(CASSANDRA_PROTOCOL_VERSION, "3"); + properties.setProperty(CASSANDRA_LOAD_BALANCING_POLICY, "DEFAULT"); + properties.setProperty(CASSANDRA_RETRY_POLICY, "DEFAULT"); + properties.setProperty(CASSANDRA_RECONNECTION_POLICY, "DEFAULT"); + properties.setProperty(CASSANDRA_SPECULATIVE_EXECUTION_POLICY, "DEFAULT"); + + properties.setProperty(CASSANDRA_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS, + DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS + ""); + + properties.setProperty(CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_LOCAL, "100"); + properties.setProperty(CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_REMOTE, "100"); + properties.setProperty(CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_LOCAL, "2"); + properties.setProperty(CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_REMOTE, "1"); + properties.setProperty(CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_LOCAL, "8"); + properties.setProperty(CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_REMOTE, "2"); + properties.setProperty(CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_LOCAL, "1024"); + properties.setProperty(CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_REMOTE, "256"); + + properties.setProperty(CASSANDRA_POOLING_IDLE_TIMEOUT_SECONDS, "120"); + properties.setProperty(CASSANDRA_POOLING_POOL_TIMEOUT_MILLIS, "5000"); + properties.setProperty(CASSANDRA_POOLING_HEARTBEAT_INTERVAL_SECONDS, "30"); + + properties.setProperty(CASSANDRA_QUERY_DEFAULT_CONSISTENCY, "ONE"); + properties.setProperty(CASSANDRA_QUERY_DEFAULT_SERIAL_CONSISTENCY, "SERIAL"); + properties.setProperty(CASSANDRA_QUERY_DEFAULT_FETCH_SIZE, "5000"); + + properties.setProperty(CASSANDRA_SOCKET_CONNECTION_TIMEOUT_MILLIS, "5000"); + properties.setProperty(CASSANDRA_SOCKET_READ_TIMEOUT_MILLIS, "12000"); + properties.setProperty(CASSANDRA_SOCKET_TCP_NO_DELAY, "true"); + + properties.setProperty(CASSANDRA_HOSTS, from(cluster.getMetadata().getAllHosts()).first().get().getAddress().getHostAddress()); + properties.setProperty(CASSANDRA_PORT, cluster.getConfiguration().getProtocolOptions().getPort()+""); + interpreter = new CassandraInterpreter(properties); + interpreter.open(); + } + + @AfterClass + public static void tearDown() { + interpreter.close(); + } + + @Before + public void prepareContext() { + when(intrContext.getParagraphTitle()).thenReturn("Paragraph1"); + } + + @Test + public void should_create_cluster_and_session_upon_call_to_open() throws Exception { + assertThat(interpreter.cluster).isNotNull(); + assertThat(interpreter.cluster.getClusterName()).isEqualTo(resource.getNativeSession().getCluster().getClusterName()); + assertThat(interpreter.session).isNotNull(); + assertThat(interpreter.helper).isNotNull(); + } + + @Test + public void should_interpret_simple_select() throws Exception { + //Given + + //When + final InterpreterResult actual = interpreter.interpret("SELECT * FROM " + ARTISTS_TABLE + " LIMIT 10;", intrContext); + + //Then + assertThat(actual).isNotNull(); + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" + + "Bogdan Raczynski\t1977-01-01\tPoland\tnull\tMale\t[Dance, Electro]\tPerson\n" + + "Krishna Das\t1947-05-31\tUSA\tnull\tMale\t[Unknown]\tPerson\n" + + "Sheryl Crow\t1962-02-11\tUSA\tnull\tFemale\t[Classic, Rock, Country, Blues, Pop, Folk]\tPerson\n" + + "Doof\t1968-08-31\tUnited Kingdom\tnull\tnull\t[Unknown]\tPerson\n" + + "House of Large Sizes\t1986-01-01\tUSA\t2003\tnull\t[Unknown]\tGroup\n" + + "Fanfarlo\t2006-01-01\tUnited Kingdom\tnull\tnull\t[Rock, Indie, Pop, Classic]\tGroup\n" + + "Jeff Beck\t1944-06-24\tUnited Kingdom\tnull\tMale\t[Rock, Pop, Classic]\tPerson\n" + + "Los Paranoias\tnull\tUnknown\tnull\tnull\t[Unknown]\tnull\n" + + "â¦And You Will Know Us by the Trail of Dead\t1994-01-01\tUSA\tnull\tnull\t[Rock, Pop, Classic]\tGroup\n"); + + } + + @Test + public void should_interpret_select_statement() throws Exception { + //Given + + //When + final InterpreterResult actual = interpreter.interpret("SELECT * FROM " + ARTISTS_TABLE + " LIMIT 2;", intrContext); + + //Then + assertThat(actual).isNotNull(); + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" + + "Bogdan Raczynski\t1977-01-01\tPoland\tnull\tMale\t[Dance, Electro]\tPerson\n" + + "Krishna Das\t1947-05-31\tUSA\tnull\tMale\t[Unknown]\tPerson\n"); + + } + + @Test + public void should_interpret_multiple_statements_with_single_line_logged_batch() throws Exception { + //Given + String statements = "CREATE TABLE IF NOT EXISTS zeppelin.albums(\n" + + " title text PRIMARY KEY,\n" + + " artist text,\n" + + " year int\n" + + ");\n" + + "BEGIN BATCH"+ + " INSERT INTO zeppelin.albums(title,artist,year) VALUES('The Impossible Dream EP','Carter the Unstoppable Sex Machine',1992);"+ + " INSERT INTO zeppelin.albums(title,artist,year) VALUES('The Way You Are','Tears for Fears',1983);"+ + " INSERT INTO zeppelin.albums(title,artist,year) VALUES('Primitive','Soulfly',2003);"+ + "APPLY BATCH;\n"+ + "SELECT * FROM zeppelin.albums;"; + //When + final InterpreterResult actual = interpreter.interpret(statements, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("title\tartist\tyear\n" + + "The Impossible Dream EP\tCarter the Unstoppable Sex Machine\t1992\n" + + "The Way You Are\tTears for Fears\t1983\n" + + "Primitive\tSoulfly\t2003\n"); + } + + @Test + public void should_throw_statement_not_having_semi_colon() throws Exception { + //Given + String statement = "SELECT * zeppelin.albums"; + + //When + final InterpreterResult actual = interpreter.interpret(statement, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()) + .contains("Error parsing input:\n" + + "\t'SELECT * zeppelin.albums'\n" + + "Did you forget to add ; (semi-colon) at the end of each CQL statement ?"); + } + + @Test + public void should_validate_statement() throws Exception { + //Given + String statement = "SELECT * zeppelin.albums;"; + + //When + final InterpreterResult actual = interpreter.interpret(statement, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()).contains("line 1:9 missing K_FROM at 'zeppelin' (SELECT * [zeppelin]....)"); + } + + @Test + public void should_execute_statement_with_consistency_option() throws Exception { + //Given + String statement = "@consistency=THREE\n" + + "SELECT * FROM zeppelin.artists LIMIT 1;"; + + //When + final InterpreterResult actual = interpreter.interpret(statement, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()) + .contains("Not enough replica available for query at consistency THREE (3 required but only 1 alive)"); + } + + @Test + public void should_execute_statement_with_serial_consistency_option() throws Exception { + //Given + String statement = "@serialConsistency=SERIAL\n" + + "SELECT * FROM zeppelin.artists LIMIT 1;"; + + //When + final InterpreterResult actual = interpreter.interpret(statement, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + } + + @Test + public void should_execute_statement_with_timestamp_option() throws Exception { + //Given + String statement1 = "INSERT INTO zeppelin.ts(key,val) VALUES('k','v1');"; + String statement2 = "@timestamp=15\n" + + "INSERT INTO zeppelin.ts(key,val) VALUES('k','v2');"; + + // Insert v1 with current timestamp + interpreter.interpret(statement1, intrContext); + + Thread.sleep(1); + + //When + // Insert v2 with past timestamp + interpreter.interpret(statement2, intrContext); + final String actual = session.execute("SELECT * FROM zeppelin.ts LIMIT 1").one().getString("val"); + + //Then + assertThat(actual).isEqualTo("v1"); + } + + @Test + public void should_execute_statement_with_retry_policy() throws Exception { + //Given + String statement = "@retryPolicy=" + interpreter.LOGGING_DOWNGRADING_RETRY + "\n" + + "@consistency=THREE\n" + + "SELECT * FROM zeppelin.artists LIMIT 1;"; + + //When + final InterpreterResult actual = interpreter.interpret(statement, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + } + + @Test + public void should_execute_prepared_and_bound_statements() throws Exception { + //Given + String queries = "@prepare[ps]=INSERT INTO zeppelin.prepared(key,val) VALUES(?,?)\n" + + "@prepare[select]=SELECT * FROM zeppelin.prepared WHERE key=:key\n" + + "@bind[ps]='myKey','myValue'\n" + + "@bind[select]='myKey'"; + + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("key\tval\n" + + "myKey\tmyValue\n"); + } + + @Test + public void should_execute_bound_statement() throws Exception { + //Given + String queries = "@prepare[users_insert]=INSERT INTO zeppelin.users" + + "(login,firstname,lastname,addresses,location)" + + "VALUES(:login,:fn,:ln,:addresses,:loc)\n" + + "@bind[users_insert]='jdoe','John','DOE'," + + "{street_number: 3, street_name: 'Beverly Hills Bld', zip_code: 90209," + + " country: 'USA', extra_info: ['Right on the hills','Next to the post box']," + + " phone_numbers: {'home': 2016778524, 'office': 2015790847}}," + + "('USA', 90209, 'Beverly Hills')\n" + + "SELECT * FROM zeppelin.users WHERE login='jdoe';"; + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo( + "login\taddresses\tage\tdeceased\tfirstname\tlast_update\tlastname\tlocation\n" + + "jdoe\t" + + "{street_number:3, street_name:'Beverly Hills Bld', zip_code:90209," + + " country:'USA', extra_info:['Right on the hills', 'Next to the post box'], " + + "phone_numbers:{'office':2015790847, 'home':2016778524}}\tnull\t" + + "null\t" + + "John\t" + + "null\t" + + "DOE\t" + + "('USA', 90209, 'Beverly Hills')\n"); + } + + @Test + public void should_exception_when_executing_unknown_bound_statement() throws Exception { + //Given + String queries = "@bind[select_users]='jdoe'"; + + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()) + .isEqualTo("The statement 'select_users' can not be bound to values. " + + "Are you sure you did prepare it with @prepare[select_users] ?"); + } + + @Test + public void should_extract_variable_from_statement() throws Exception { + //Given + when(intrContext.getGui().input("login", "hsue")).thenReturn("hsue"); + when(intrContext.getGui().input("age", "27")).thenReturn("27"); + + String queries = "@prepare[test_insert_with_variable]=" + + "INSERT INTO zeppelin.users(login,firstname,lastname,age) VALUES(?,?,?,?)\n" + + "@bind[test_insert_with_variable]='{{login=hsue}}','Helen','SUE',{{age=27}}\n" + + "SELECT firstname,lastname,age FROM zeppelin.users WHERE login='hsue';"; + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("firstname\tlastname\tage\n" + + "Helen\tSUE\t27\n"); + + } + + @Test + public void should_just_prepare_statement() throws Exception { + //Given + String queries = "@prepare[just_prepare]=SELECT name,country,styles FROM zeppelin.artists LIMIT 3"; + final String expected = reformatHtml( + readTestResource("/scalate/NoResult.html")); + + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_execute_bound_statement_with_no_bound_value() throws Exception { + //Given + String queries = "@prepare[select_no_bound_value]=SELECT name,country,styles FROM zeppelin.artists LIMIT 3\n" + + "@bind[select_no_bound_value]"; + + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("name\tcountry\tstyles\n" + + "Bogdan Raczynski\tPoland\t[Dance, Electro]\n" + + "Krishna Das\tUSA\t[Unknown]\n" + + "Sheryl Crow\tUSA\t[Classic, Rock, Country, Blues, Pop, Folk]\n"); + + } + + @Test + public void should_parse_date_value() throws Exception { + //Given + String queries = "@prepare[parse_date]=INSERT INTO zeppelin.users(login,last_update) VALUES(?,?)\n" + + "@bind[parse_date]='last_update','2015-07-30 12:00:01'\n" + + "SELECT last_update FROM zeppelin.users WHERE login='last_update';"; + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).contains("last_update\n" + + "Thu Jul 30 12:00:01"); + } + + @Test + public void should_bind_null_value() throws Exception { + //Given + String queries = "@prepare[bind_null]=INSERT INTO zeppelin.users(login,firstname,lastname) VALUES(?,?,?)\n" + + "@bind[bind_null]='bind_null',null,'NULL'\n" + + "SELECT firstname,lastname FROM zeppelin.users WHERE login='bind_null';"; + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("firstname\tlastname\n" + + "null\tNULL\n"); + } + + @Test + public void should_bind_boolean_value() throws Exception { + //Given + String queries = "@prepare[bind_boolean]=INSERT INTO zeppelin.users(login,deceased) VALUES(?,?)\n" + + "@bind[bind_boolean]='bind_bool',false\n" + + "SELECT login,deceased FROM zeppelin.users WHERE login='bind_bool';"; + //When + final InterpreterResult actual = interpreter.interpret(queries, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("login\tdeceased\n" + + "bind_bool\tfalse\n"); + } + + @Test + public void should_fail_when_executing_a_removed_prepared_statement() throws Exception { + //Given + String prepare_first = "@prepare[to_be_removed]=INSERT INTO zeppelin.users(login,deceased) VALUES(?,?)"; + interpreter.interpret(prepare_first, intrContext); + String remove_prepared = "@remove_prepare[to_be_removed]\n" + + "@bind[to_be_removed]='bind_bool'"; + + //When + final InterpreterResult actual = interpreter.interpret(remove_prepared, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()).isEqualTo("The statement 'to_be_removed' can not be bound to values. " + + "Are you sure you did prepare it with @prepare[to_be_removed] ?"); + } + + @Test + public void should_display_statistics_for_non_select_statement() throws Exception { + //Given + String query = "USE zeppelin;\nCREATE TABLE IF NOT EXISTS no_select(id int PRIMARY KEY);"; + final String rawResult = reformatHtml(readTestResource("/scalate/NoResultWithExecutionInfo.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + final Cluster cluster = session.getCluster(); + final int port = cluster.getConfiguration().getProtocolOptions().getPort(); + final String address = cluster.getMetadata().getAllHosts().iterator().next() + .getAddress().getHostAddress() + .replaceAll("/", "").replaceAll("\\[", "").replaceAll("\\]",""); + //Then + final String expected = rawResult.replaceAll("TRIED_HOSTS", address+":"+port) + .replaceAll("QUERIED_HOSTS", address +":"+port); + + + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_error_and_display_stack_trace() throws Exception { + //Given + String query = "@consistency=THREE\n" + + "SELECT * FROM zeppelin.users LIMIT 3;"; + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()).contains("All host(s) tried for query failed"); + } + + @Test + public void should_describe_cluster() throws Exception { + //Given + + String query = "DESCRIBE CLUSTER;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeCluster.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_describe_keyspaces() throws Exception { + //Given + String query = "DESCRIBE KEYSPACES;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeKeyspaces.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_describe_keyspace() throws Exception { + //Given + String query = "DESCRIBE KEYSPACE live_data;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeKeyspace_live_data.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_describe_table() throws Exception { + //Given + String query = "DESCRIBE TABLE live_data.complex_table;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeTable_live_data_complex_table.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_describe_udt() throws Exception { + //Given + String query = "DESCRIBE TYPE live_data.address;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeType_live_data_address.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + @Test + public void should_describe_udt_withing_logged_in_keyspace() throws Exception { + //Given + String query = "USE live_data;\n" + + "DESCRIBE TYPE address;"; + final String expected = reformatHtml( + readTestResource("/scalate/DescribeType_live_data_address_within_current_keyspace.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + @Test + public void should_error_describing_non_existing_table() throws Exception { + //Given + String query = "USE system;\n" + + "DESCRIBE TABLE complex_table;"; + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()).contains("Cannot find table system.complex_table"); + } + + @Test + public void should_error_describing_non_existing_udt() throws Exception { + //Given + String query = "USE system;\n" + + "DESCRIBE TYPE address;"; + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.ERROR); + assertThat(actual.message()).contains("Cannot find type system.address"); + } + + @Test + public void should_show_help() throws Exception { + //Given + String query = "HELP;"; + final String expected = reformatHtml(readTestResource("/scalate/Help.html")); + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(reformatHtml(actual.message())).isEqualTo(expected); + } + + private static String reformatHtml(String rawHtml) { + return rawHtml + .replaceAll("\\s*\n\\s*","") + .replaceAll(">\\s+<", "><") + .replaceAll("(?s)data-target=\"#[a-f0-9-]+(?:_asCQL)?\"", "") + .replaceAll("(?s)id=\"[a-f0-9-]+(?:_asCQL)?\"", "") + .trim(); + } + + private static String readTestResource(String testResource) { + StringBuilder builder = new StringBuilder(); + InputStream stream = testResource.getClass().getResourceAsStream(testResource); + + try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) { + String line; + while ((line = br.readLine()) != null) { + builder.append(line).append("\n"); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + return builder.toString(); + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java ---------------------------------------------------------------------- diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java new file mode 100644 index 0000000..f7993fb --- /dev/null +++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java @@ -0,0 +1,311 @@ +/* + * 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.cassandra; + +import static com.datastax.driver.core.BatchStatement.Type.UNLOGGED; +import static com.datastax.driver.core.ConsistencyLevel.ALL; +import static com.datastax.driver.core.ConsistencyLevel.LOCAL_SERIAL; +import static com.datastax.driver.core.ConsistencyLevel.ONE; +import static com.datastax.driver.core.ConsistencyLevel.QUORUM; +import static com.datastax.driver.core.ConsistencyLevel.SERIAL; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.Statement; +import org.apache.zeppelin.display.GUI; +import org.apache.zeppelin.display.Input.ParamOption; +import org.apache.zeppelin.interpreter.InterpreterContext; +import org.apache.zeppelin.interpreter.InterpreterException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import scala.Option; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.apache.zeppelin.cassandra.TextBlockHierarchy.*; + +@RunWith(MockitoJUnitRunner.class) +public class InterpreterLogicTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private InterpreterContext intrContext; + + @Mock + private Session session; + + final InterpreterLogic helper = new InterpreterLogic(session); + + @Captor + ArgumentCaptor<ParamOption[]> optionsCaptor; + + @Test + public void should_parse_input_string_block() throws Exception { + //Given + String input = "SELECT * FROM users LIMIT 10;"; + + //When + final List<AnyBlock> anyBlocks = this.<AnyBlock>toJavaList(helper.parseInput(input)); + + //Then + assertThat(anyBlocks).hasSize(1); + assertThat(anyBlocks.get(0)).isInstanceOf(SimpleStm.class); + } + + @Test + public void should_exception_while_parsing_input() throws Exception { + //Given + String input = "SELECT * FROM users LIMIT 10"; + + //When + expectedException.expect(InterpreterException.class); + expectedException.expectMessage("Error parsing input:\n" + + "\t'SELECT * FROM users LIMIT 10'\n" + + "Did you forget to add ; (semi-colon) at the end of each CQL statement ?"); + + helper.parseInput(input); + } + + @Test + public void should_extract_variable_and_default_value() throws Exception { + //Given + when(intrContext.getGui().input("table", "zeppelin.demo")).thenReturn("zeppelin.demo"); + when(intrContext.getGui().input("id", "'John'")).thenReturn("'John'"); + + //When + final String actual = helper.maybeExtractVariables("SELECT * FROM {{table=zeppelin.demo}} WHERE id={{id='John'}}", intrContext); + + //Then + assertThat(actual).isEqualTo("SELECT * FROM zeppelin.demo WHERE id='John'"); + } + + @Test + public void should_extract_variable_and_choices() throws Exception { + //Given + when(intrContext.getGui().select(eq("name"), eq("'Paul'"), optionsCaptor.capture())).thenReturn("'Jack'"); + + //When + final String actual = helper.maybeExtractVariables("SELECT * FROM zeppelin.artists WHERE name={{name='Paul'|'Jack'|'Smith'}}", intrContext); + + //Then + assertThat(actual).isEqualTo("SELECT * FROM zeppelin.artists WHERE name='Jack'"); + final List<ParamOption> paramOptions = asList(optionsCaptor.getValue()); + assertThat(paramOptions.get(0).getValue()).isEqualTo("'Paul'"); + assertThat(paramOptions.get(1).getValue()).isEqualTo("'Jack'"); + assertThat(paramOptions.get(2).getValue()).isEqualTo("'Smith'"); + } + + @Test + public void should_extract_no_variable() throws Exception { + //Given + GUI gui = mock(GUI.class); + when(intrContext.getGui()).thenReturn(gui); + + //When + final String actual = helper.maybeExtractVariables("SELECT * FROM zeppelin.demo", intrContext); + + //Then + verifyZeroInteractions(gui); + assertThat(actual).isEqualTo("SELECT * FROM zeppelin.demo"); + } + + @Test + public void should_error_if_incorrect_variable_definition() throws Exception { + //Given + + //When + expectedException.expect(ParsingException.class); + expectedException.expectMessage("Invalid bound variable definition for '{{table?zeppelin.demo}}' in 'SELECT * FROM {{table?zeppelin.demo}} WHERE id={{id='John'}}'. It should be of form 'variable=defaultValue'"); + + //Then + helper.maybeExtractVariables("SELECT * FROM {{table?zeppelin.demo}} WHERE id={{id='John'}}", intrContext); + } + + + @Test + public void should_extract_consistency_option() throws Exception { + //Given + List<QueryParameters> options = Arrays.<QueryParameters>asList(new Consistency(ALL), new Consistency(ONE)); + + //When + final CassandraQueryOptions actual = helper.extractQueryOptions(toScalaList(options)); + + //Then + assertThat(actual.consistency().get()).isEqualTo(ALL); + } + + + @Test + public void should_extract_serial_consistency_option() throws Exception { + //Given + List<QueryParameters> options = Arrays.<QueryParameters>asList(new SerialConsistency(SERIAL), new SerialConsistency(LOCAL_SERIAL)); + + //When + final CassandraQueryOptions actual = helper.extractQueryOptions(toScalaList(options)); + + //Then + assertThat(actual.serialConsistency().get()).isEqualTo(SERIAL); + } + + @Test + public void should_extract_timestamp_option() throws Exception { + //Given + List<QueryParameters> options = Arrays.<QueryParameters>asList(new Timestamp(123L), new Timestamp(456L)); + + //When + final CassandraQueryOptions actual = helper.extractQueryOptions(toScalaList(options)); + + //Then + assertThat(actual.timestamp().get()).isEqualTo(123L); + } + + @Test + public void should_extract_retry_policy_option() throws Exception { + //Given + List<QueryParameters> options = Arrays.<QueryParameters>asList(DowngradingRetryPolicy$.MODULE$, LoggingDefaultRetryPolicy$.MODULE$); + + //When + final CassandraQueryOptions actual = helper.extractQueryOptions(toScalaList(options)); + + //Then + assertThat(actual.retryPolicy().get()).isSameAs(DowngradingRetryPolicy$.MODULE$); + } + + @Test + public void should_generate_simple_statement() throws Exception { + //Given + String input = "SELECT * FROM users LIMIT 10;"; + CassandraQueryOptions options = new CassandraQueryOptions(Option.apply(QUORUM), + Option.<ConsistencyLevel>empty(), + Option.empty(), + Option.<RetryPolicy>empty(), + Option.empty()); + + //When + final SimpleStatement actual = helper.generateSimpleStatement(new SimpleStm(input), options, intrContext); + + //Then + assertThat(actual).isNotNull(); + assertThat(actual.getQueryString()).isEqualTo("SELECT * FROM users LIMIT 10;"); + assertThat(actual.getConsistencyLevel()).isSameAs(QUORUM); + } + + @Test + public void should_generate_batch_statement() throws Exception { + //Given + Statement st1 = new SimpleStatement("SELECT * FROM users LIMIT 10;"); + Statement st2 = new SimpleStatement("INSERT INTO users(id) VALUES(10);"); + Statement st3 = new SimpleStatement("UPDATE users SET name = 'John DOE' WHERE id=10;"); + CassandraQueryOptions options = new CassandraQueryOptions(Option.apply(QUORUM), + Option.<ConsistencyLevel>empty(), + Option.empty(), + Option.<RetryPolicy>empty(), + Option.empty()); + + //When + BatchStatement actual = helper.generateBatchStatement(UNLOGGED, options, toScalaList(asList(st1, st2, st3))); + + //Then + assertThat(actual).isNotNull(); + final List<Statement> statements = new ArrayList<>(actual.getStatements()); + assertThat(statements).hasSize(3); + assertThat(statements.get(0)).isSameAs(st1); + assertThat(statements.get(1)).isSameAs(st2); + assertThat(statements.get(2)).isSameAs(st3); + assertThat(actual.getConsistencyLevel()).isSameAs(QUORUM); + } + + @Test + public void should_parse_bound_values() throws Exception { + //Given + String bs="'jdoe',32,'John DOE',null, true, '2014-06-12 34:00:34'"; + + //When + final List<String> actual = this.<String>toJavaList(helper.parseBoundValues("ps", bs)); + + //Then + assertThat(actual).containsExactly("'jdoe'", "32", "'John DOE'", + "null", "true", "2014-06-12 34:00:34"); + } + + @Test + public void should_parse_simple_date() throws Exception { + //Given + String dateString = "2015-07-30 12:00:01"; + + //When + final Date actual = helper.parseDate(dateString); + + //Then + Calendar calendar = Calendar.getInstance(); + calendar.setTime(actual); + + assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2015); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JULY); + assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(30); + assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(12); + assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(0); + assertThat(calendar.get(Calendar.SECOND)).isEqualTo(1); + } + + @Test + public void should_parse_accurate_date() throws Exception { + //Given + String dateString = "2015-07-30 12:00:01.123"; + + //When + final Date actual = helper.parseDate(dateString); + + //Then + Calendar calendar = Calendar.getInstance(); + calendar.setTime(actual); + + assertThat(calendar.get(Calendar.YEAR)).isEqualTo(2015); + assertThat(calendar.get(Calendar.MONTH)).isEqualTo(Calendar.JULY); + assertThat(calendar.get(Calendar.DAY_OF_MONTH)).isEqualTo(30); + assertThat(calendar.get(Calendar.HOUR_OF_DAY)).isEqualTo(12); + assertThat(calendar.get(Calendar.MINUTE)).isEqualTo(0); + assertThat(calendar.get(Calendar.SECOND)).isEqualTo(1); + assertThat(calendar.get(Calendar.MILLISECOND)).isEqualTo(123); + } + + private <A> scala.collection.immutable.List<A> toScalaList(java.util.List<A> list) { + return scala.collection.JavaConverters.asScalaBufferConverter(list).asScala().toList(); + } + + private <A> java.util.List<A> toJavaList(scala.collection.immutable.List<A> list){ + return scala.collection.JavaConverters.seqAsJavaListConverter(list).asJava(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/resources/log4j.xml ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/log4j.xml b/cassandra/src/test/resources/log4j.xml new file mode 100644 index 0000000..279a4f5 --- /dev/null +++ b/cassandra/src/test/resources/log4j.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2012-2014 DuyHai DOAN + ~ + ~ 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. + --> + +<!DOCTYPE log4j:configuration PUBLIC + "-//APACHE//DTD LOG4J 1.2//EN" + "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd"> +<log4j:configuration debug="false" + xmlns:log4j="http://jakarta.apache.org/log4j/"> + <!-- Appenders --> + <appender name="ConsoleAppender" + class="org.apache.log4j.ConsoleAppender"> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" + value="%-5p [%d{ABSOLUTE}][%x] %c@:%M %m %n"/> + </layout> + </appender> + + <logger name="ACHILLES_DDL_SCRIPT"> + <level value="DEBUG"/> + </logger> + + <logger name="ACHILLES_DML_STATEMENT"> + <level value="DEBUG"/> + </logger> + + <logger name="info.archinnov.achilles"> + <level value="INFO"/> + </logger> + + <logger name="org.apache.zeppelin.cassandra"> + <level value="DEBUG"/> + </logger> + + <!-- lots of warning for embedded Cassandra, shut them up --> + <logger name="org.apache.cassandra.cql3.QueryProcessor"> + <level value="OFF"/> + </logger> + <logger name="org.apache.cassandra.service.CassandraDaemon"> + <level value="OFF"/> + </logger> + <logger name="com.datastax.driver.core.ControlConnection"> + <level value="OFF"/> + </logger> + <logger name="org.apache.cassandra.db.Memtable"> + <level value="OFF"/> + </logger> + <logger name="org.apache.cassandra.utils.CLibrary"> + <level value="OFF"/> + </logger> + <logger name="com.datastax.driver.core.NettyUtil"> + <level value="OFF"/> + </logger> + + <logger name="io.netty"> + <level value="OFF"/> + </logger> + + + <!-- ********************************************************* --> + <!-- Root Loggers --> + <!-- ********************************************************* --> + <root> + <level value="WARN"/> + <appender-ref ref="ConsoleAppender"/> + </root> +</log4j:configuration> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/resources/prepare_data.cql ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/prepare_data.cql b/cassandra/src/test/resources/prepare_data.cql new file mode 100644 index 0000000..590a97c --- /dev/null +++ b/cassandra/src/test/resources/prepare_data.cql @@ -0,0 +1,18 @@ + +TRUNCATE zeppelin.artists; + +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Bogdan Raczynski','1977-01-01','Poland',null,'Male',['Dance', 'Electro'],'Person'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Krishna Das','1947-05-31','USA',null,'Male',['Unknown'],'Person'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Sheryl Crow','1962-02-11','USA',null,'Female',['Classic', 'Rock', 'Country', 'Blues', 'Pop', 'Folk'],'Person'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Doof','1968-08-31','United Kingdom',null,null,['Unknown'],'Person'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('House of Large Sizes','1986-01-01','USA','2003',null,['Unknown'],'Group'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Fanfarlo','2006-01-01','United Kingdom',null,null,['Rock', 'Indie', 'Pop', 'Classic'],'Group'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Jeff Beck','1944-06-24','United Kingdom',null,'Male',['Rock', 'Pop', 'Classic'],'Person'); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('Los Paranoias',null,'Unknown',null,null,['Unknown'],null); +INSERT INTO zeppelin.artists(name,born,country,died,gender,styles,type) VALUES('â¦And You Will Know Us by the Trail of Dead','1994-01-01','USA',null,null,['Rock', 'Pop', 'Classic'],'Group'); + +TRUNCATE zeppelin.ts; + +TRUNCATE zeppelin.prepared; + +TRUNCATE zeppelin.users; http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/resources/prepare_schema.cql ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/prepare_schema.cql b/cassandra/src/test/resources/prepare_schema.cql new file mode 100644 index 0000000..bdf9773 --- /dev/null +++ b/cassandra/src/test/resources/prepare_schema.cql @@ -0,0 +1,114 @@ +CREATE KEYSPACE IF NOT EXISTS zeppelin + WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1} + AND DURABLE_WRITES=false; + +CREATE TABLE IF NOT EXISTS zeppelin.artists( + name text PRIMARY KEY, + born text, + died text, + country text, + gender text, + type text, + styles list<text> +); + +CREATE TABLE IF NOT EXISTS zeppelin.ts( + key text PRIMARY KEY, + val text +); + +CREATE TABLE IF NOT EXISTS zeppelin.prepared( + key text PRIMARY KEY, + val text +); + +CREATE TYPE IF NOT EXISTS zeppelin.address( + street_number int, + street_name text, + zip_code int, + country text, + extra_info list<text>, + phone_numbers map<text,bigint> + +); + + +CREATE TABLE IF NOT EXISTS zeppelin.users( + login text, + firstname text, + lastname text, + age int, + deceased boolean, + last_update timestamp, + addresses frozen<address>, + location frozen<tuple<text,bigint,text>>, + PRIMARY KEY(login) +); + +CREATE KEYSPACE IF NOT EXISTS samples + WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1} + AND DURABLE_WRITES=false; + +CREATE KEYSPACE IF NOT EXISTS live_data + WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1} + AND DURABLE_WRITES=false; + +CREATE TYPE IF NOT EXISTS live_data.address ( + number int, + street text, + zip int, + city text, + country text +); + +CREATE TABLE IF NOT EXISTS live_data.complex_table( + pk1 uuid, + pk2 int, + my_static1 text static, + my_static2 text static, + clustering1 timestamp, + clustering2 int, + clustering3 text, + indexed1 text, + indexed2 int, + simple double, + my_list list<text>, + my_udt_list frozen<list<address>>, + my_udt frozen<address>, + my_map map<int,text>, + key_indexed_map map<int,text>, + entries_indexed_map map<int,text>, + PRIMARY KEY((pk1,pk2),clustering1, clustering2, clustering3) +) WITH CLUSTERING ORDER BY (clustering1 DESC, clustering2 ASC, clustering3 DESC); + +CREATE INDEX IF NOT EXISTS pk2idx ON live_data.complex_table(pk2); +CREATE INDEX IF NOT EXISTS clustering2idx ON live_data.complex_table(clustering2); +CREATE INDEX IF NOT EXISTS idx1 ON live_data.complex_table(indexed1); +CREATE INDEX IF NOT EXISTS idx2 ON live_data.complex_table(indexed2); +CREATE INDEX IF NOT EXISTS keys_map_idx ON live_data.complex_table(KEYS(key_indexed_map)); + + + + +CREATE TABLE IF NOT EXISTS live_data.sensor_data ( + sensor_id uuid, + month int, + provider text static, + model_number text static, + characteristics map<text, text> static, + date timestamp, + value double, + PRIMARY KEY((sensor_id, month), date) +) WITH CLUSTERING ORDER BY (date DESC); + + +CREATE TYPE IF NOT EXISTS live_data.geolocation ( + latitude double, + longitude double +); + +CREATE TABLE IF NOT EXISTS live_data.stations ( + station_id uuid, + sensors frozen<map<uuid,geolocation>>, + PRIMARY KEY (station_id) +); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/resources/scalate/DescribeCluster.html ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/scalate/DescribeCluster.html b/cassandra/src/test/resources/scalate/DescribeCluster.html new file mode 100644 index 0000000..5d32c7d --- /dev/null +++ b/cassandra/src/test/resources/scalate/DescribeCluster.html @@ -0,0 +1,98 @@ +<br/> +<br/> +<nav class="navbar navbar-default"> + <ul class="nav navbar-nav"> + + <li> + <a><strong>DESCRIBE CLUSTER;</strong></a> + </li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li class="dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> + <strong>Legend</strong> + <span class="caret"></span> + </a> + <ul class="dropdown-menu"> + <li> + <a role="button"> + <i class="glyphicon glyphicon-dashboard text-muted" /> Cluster + </a> + </li> + <li> + <a role="button"> + <i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace + </a> + </li> + <li> + <a role="button"> + <i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT + </a> + </li> + <li> + <a role="button"> + <i class="glyphicon glyphicon-th-list text-primary" /> Table + </a> + </li> + <li class="bg-info"> + <a role="button"> + <i class="glyphicon glyphicon-fullscreen" /> Partition Key + </a> + </li> + <li class="bg-warning"> + <a role="button"> + <i class="glyphicon glyphicon-pushpin" /> Static Column + </a> + </li> + <li class="bg-success"> + <a role="button"> + <i class="glyphicon glyphicon-sort" /> Clustering Column + </a> + </li> + <li class="bg-success"> + <a role="button"> + <i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC + </a> + </li> + <li class="bg-success"> + <a role="button"> + <i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC + </a> + </li> + <li> + <a role="button"> + <i class="glyphicon glyphicon-info-sign" /> Indexed Column + </a> + </li> + </ul> + </li> + <li> + <a href="#"></a> + </li> + </ul> +</nav> +<hr/> +<div class="row"> + <div class="col-md-4"></div> + <div class="col-md-4 col-offset-md-4"> + <div class="table-responsive table-bordered"> + <table class="table"> + <caption> + <h4 class="text-muted"> + <i class="glyphicon glyphicon-dashboard"/> Test Cluster + </h4> + </caption> + <thead> + <tr> + <th>Partitioner</th> + </tr> + </thead> + <tbody> + <tr> + <td>org.apache.cassandra.dht.Murmur3Partitioner</td> + </tr> + <tbody> + </table> + </div> + </div> +</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/test/resources/scalate/DescribeKeyspace_alone.html ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/scalate/DescribeKeyspace_alone.html b/cassandra/src/test/resources/scalate/DescribeKeyspace_alone.html new file mode 100644 index 0000000..97fc9bc --- /dev/null +++ b/cassandra/src/test/resources/scalate/DescribeKeyspace_alone.html @@ -0,0 +1,41 @@ +<div class="row"> + <div class="col-md-2"></div> + <div class="col-md-8 col-offset-md-2"> + <div class="panel panel-default table-responsive table-bordered"> + <table class="table"> + + <caption> + <h4 class="text-danger"> + <i class="glyphicon glyphicon-folder-open"/> live_data + </h4> + </caption> + <thead> + <tr> + <th class="col-md-10">Replication</th> + <th class="col-md-2">Durable Writes</th> + </tr> + </thead> + <tbody> + <tr> + <td class="col-md-10">{'replication_factor' : '1', 'class' : 'org.apache.cassandra.locator.SimpleStrategy'}</td> + <td class="col-md-2">false</td> + </tr> + <tbody> + </table> + <div class="panel-footer"> + <a data-toggle="collapse" data-target="#3b294060-3516-11e5-ab36-8f0ea8ae1a37_asCQL"> + <strong>As CQL statement</strong> + <span class="caret"></span> + </a> + <br/><br/> + <div class="collapse" id="3b294060-3516-11e5-ab36-8f0ea8ae1a37_asCQL"> + <pre class="well">CREATE KEYSPACE live_data WITH REPLICATION = { + 'class' : 'org.apache.cassandra.locator.SimpleStrategy', + 'replication_factor': '1' } +AND DURABLE_WRITES = false;</pre> + </div> + </div> + </div> + </div> + <div class="col-md-2"></div> +</div> \ No newline at end of file
