/* * Copyright (C) 2012-2015 DataStax Inc. * * 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. */ package com.datastax.driver.extras.codecs.json; import com.datastax.driver.core.*; import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.utils.CassandraVersion; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import javax.json.*; import java.io.IOException; import java.io.StringWriter; import static com.datastax.driver.core.querybuilder.QueryBuilder.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; public class Jsr353JsonCodecTest extends CCMTestsSupport { private static final Jsr353JsonCodec jsonCodec = new Jsr353JsonCodec(); private static final JsonObject alice = Json.createObjectBuilder().add("id", 1).add("name", "Alice").build(); private static final JsonObject bob = Json.createObjectBuilder().add("id", 2).add("name", "Bob").build(); private static final JsonObject charlie = Json.createObjectBuilder().add("id", 3).add("name", "Charlie").build(); private static final JsonArray bobAndCharlie = Json.createArrayBuilder().add(bob).add(charlie).build(); private static final String insertQuery = "INSERT INTO t1 (c1, c2) VALUES (?, ?)"; private static final String selectQuery = "SELECT c1, c2 FROM t1 WHERE c1 = ? and c2 = ?"; private static final String notAJsonString = "this text is not json"; private static final String TABLE1 = "test1"; private static final String TABLE2 = "test2"; @Override public void onTestContextInitialized() { execute( "CREATE TABLE t1 (c1 text, c2 text, PRIMARY KEY (c1, c2))", String.format("CREATE TABLE %s (k text, \"miXeD\" text, i int, f float, PRIMARY KEY (k, \"miXeD\"))", TABLE1), insertInto(TABLE1).value("k", "key0").value(quote("miXeD"), "a").value("i", 1).value("f", 1.1).toString(), insertInto(TABLE1).value("k", "key0").value(quote("miXeD"), "b").value("i", 2).value("f", 2.5).toString(), insertInto(TABLE1).value("k", "key0").value(quote("miXeD"), "c").value("i", 3).value("f", 3.7).toString(), insertInto(TABLE1).value("k", "key0").value(quote("miXeD"), "d").value("i", 4).value("f", 5.0).toString() ); if (ccm().getProtocolVersion().compareTo(ProtocolVersion.V3) >= 0) { execute( "CREATE TYPE address (street text, zipcode int, phones list<text>)", String.format("CREATE TABLE %s (k text PRIMARY KEY, v frozen<address>)", TABLE2) ); } } @Override public Cluster.Builder createClusterBuilder() { return Cluster.builder().withCodecRegistry( new CodecRegistry().register(jsonCodec) // global JsonStructure <-> varchar codec ); } @DataProvider(name = "Jsr353JsonCodecTest") public static Object[][] parameters() { return new Object[][]{ {alice}, {bobAndCharlie} }; } @Test(groups = "unit") public void test_cql_text_to_json() { Jsr353JsonCodec codec = new Jsr353JsonCodec(); String json = "'{\"id\":1,\"name\":\"John Doe\"}'"; JsonObject object = Json.createObjectBuilder().add("id", 1).add("name", "John Doe").build(); assertThat(codec.format(object)).isEqualTo(json); assertThat(codec.parse(json)).isEqualTo(object); } @Test(groups = "unit") public void test_nulls() { Jsr353JsonCodec codec = new Jsr353JsonCodec(); assertThat(codec.format(null)).isEqualTo("NULL"); assertThat(codec.parse(null)).isNull(); } @Test(groups = "short", dataProvider = "Jsr353JsonCodecTest") @CassandraVersion("2.0.0") public void should_use_custom_codec_with_simple_statements(JsonStructure object) throws IOException { session().execute(insertQuery, notAJsonString, object); ResultSet rows = session().execute(selectQuery, notAJsonString, object); Row row = rows.one(); assertRow(row, object); } @Test(groups = "short", dataProvider = "Jsr353JsonCodecTest") public void should_use_custom_codec_with_built_statements_1(JsonStructure object) throws IOException { BuiltStatement insertStmt = insertInto("t1") .value("c1", bindMarker()) .value("c2", bindMarker()); BuiltStatement selectStmt = select("c1", "c2") .from("t1") .where(eq("c1", bindMarker())) .and(eq("c2", bindMarker())); session().execute(session().prepare(insertStmt).bind(notAJsonString, object)); ResultSet rows = session().execute(session().prepare(selectStmt).bind(notAJsonString, object)); Row row = rows.one(); assertRow(row, object); } @Test(groups = "short", dataProvider = "Jsr353JsonCodecTest") public void should_use_custom_codec_with_built_statements_2(JsonStructure object) throws IOException { BuiltStatement insertStmt = insertInto("t1") .value("c1", notAJsonString) .value("c2", object); BuiltStatement selectStmt = select("c1", "c2") .from("t1") .where(eq("c1", notAJsonString)) .and(eq("c2", object)); session().execute(insertStmt); ResultSet rows = session().execute(selectStmt); Row row = rows.one(); assertRow(row, object); } @Test(groups = "short", dataProvider = "Jsr353JsonCodecTest") public void should_use_custom_codec_with_prepared_statements_1(JsonStructure object) throws IOException { session().execute(session().prepare(insertQuery).bind(notAJsonString, object)); PreparedStatement ps = session().prepare(selectQuery); ResultSet rows = session().execute(ps.bind(notAJsonString, object)); Row row = rows.one(); assertRow(row, object); } @Test(groups = "short", dataProvider = "Jsr353JsonCodecTest") public void should_use_custom_codec_with_prepared_statements_2(JsonStructure object) throws IOException { session().execute(session().prepare(insertQuery).bind() .setString(0, notAJsonString) .set(1, object, JsonStructure.class) ); PreparedStatement ps = session().prepare(selectQuery); ResultSet rows = session().execute(ps.bind() .setString(0, notAJsonString) .set(1, object, JsonStructure.class) ); Row row = rows.one(); assertRow(row, object); } private void assertRow(Row row, JsonStructure object) throws IOException { String json; StringWriter sw = new StringWriter(); JsonWriter writer = Json.createWriter(sw); writer.write(object); json = sw.toString(); assertThat(row.getString(0)).isEqualTo(notAJsonString); assertThat(row.getObject(1)).isEqualTo(json); assertThat(row.getString(1)).isEqualTo(json); assertThat(row.get(1, String.class)).isEqualTo(json); assertThat(row.get(1, JsonStructure.class)).isEqualTo(object); } /** * Validates that the {@code select().json()} capability in {@link QueryBuilder} appropriately generates a * 'SELECT JSON' query and that the returned {@link ResultSet} has rows with a column that is parsable using * {@link Jsr353JsonCodec}. * * @jira_ticket JAVA-743 * @since 3.1.0 */ @Test(groups = "short") @CassandraVersion("2.2.0") public void should_support_select_json_output() throws Exception { // when ResultSet r = session().execute(select().json().from(TABLE1).where(eq("k", "key0"))); // then assertThat(r.getAvailableWithoutFetching()).isEqualTo(4); for (Row row : r) { JsonStructure json = row.get(0, jsonCodec); assertThat(json).isInstanceOf(JsonObject.class); JsonObject obj = (JsonObject) json; assertThat(obj.containsKey("k")).isTrue(); assertThat(obj.containsKey(quote("miXeD"))).isTrue(); assertThat(obj.containsKey("i")).isTrue(); assertThat(obj.containsKey("f")).isTrue(); assertThat(obj.getString("k")).isEqualTo("key0"); int i = obj.getInt("i"); switch (i) { case 1: assertThat(obj.getString(quote("miXeD"))).isEqualTo("a"); assertThat(obj.getJsonNumber("f").doubleValue()).isEqualTo(1.1); break; case 2: assertThat(obj.getString(quote("miXeD"))).isEqualTo("b"); assertThat(obj.getJsonNumber("f").doubleValue()).isEqualTo(2.5); break; case 3: assertThat(obj.getString(quote("miXeD"))).isEqualTo("c"); assertThat(obj.getJsonNumber("f").doubleValue()).isEqualTo(3.7); break; case 4: assertThat(obj.getString(quote("miXeD"))).isEqualTo("d"); assertThat(obj.getJsonNumber("f").doubleValue()).isEqualTo(5.0); break; default: fail("Unexpected value for i = " + i); } } } /** * Validates that the {@code insertInto().json()} capability in {@link QueryBuilder} creates a query that interprets * the input as a json document and that fetching that data back as json returns a row with a column that matches * the contents of the input json document when retrieved using get with {@link JsonStructure}. * * @jira_ticket JAVA-743 * @since 3.1.0 */ @Test(groups = "short") @CassandraVersion("2.2.0") public void should_support_insert_json() throws Exception { // when String key = "should_support_insert_json_with_codec_format"; JsonObject input = Json.createObjectBuilder().add("k", key) .add(quote("miXeD"), "miXeDValue") .add("f", 8.6) .add("i", 4) .build(); session().execute(insertInto(TABLE1).json(input)); // then ResultSet r = session().execute(select().json().from(TABLE1).where(eq("k", key))); assertThat(r.getAvailableWithoutFetching()).isEqualTo(1); JsonStructure output = r.one().get(0, JsonStructure.class); assertThat(output).isEqualTo(input); } /** * Validates that the {@code fromJson()} and {@code toJson} capability in {@link QueryBuilder} allows specifying * json input ({@code fromJson}) and retrieval of json column output ({@code toJson}). This test inserts a row * with a UDT providing the UDT column value as json using {@code toJson} and then retrieves that column value * using {@code fromJson} and ensures the output matches the input {@link JsonStructure} for the UDT. * * @jira_ticket JAVA-743 * @since 3.1.0 */ @Test(groups = "short") @CassandraVersion("2.2.0") public void should_support_fromJson_and_toJson() throws Exception { String key = "should_support_fromJson_and_toJson"; JsonObject inputAddr = Json.createObjectBuilder() .add("street", "1234 Ln.") .add("zipcode", 86753) .add("phones", Json.createArrayBuilder().add("555-5555").add("867-5309").build()) .build(); // insert using fromJson on address column, which is a UDT. session().execute(insertInto(TABLE2).value("k", key).value("v", fromJson(inputAddr))); // retrieve using toJson on address column. ResultSet r = session().execute(select().toJson("v").as("v").column("k").from(TABLE2).where(eq("k", key))); assertThat(r.getAvailableWithoutFetching()).isEqualTo(1); Row row = r.one(); assertThat(row.getString("k")).isEqualTo(key); assertThat(row.get("v", JsonStructure.class)).isEqualTo(inputAddr); } }