blob: 9b6ed735d40170200a794c50be3021ee3956d98e [file]
/*
* Copyright 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.oss.driver.querybuilder;
import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker;
import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.insertInto;
import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom;
import static com.datastax.oss.driver.assertions.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException;
import com.datastax.oss.driver.api.core.type.codec.ExtraTypeCodecs;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.testinfra.CassandraRequirement;
import com.datastax.oss.driver.api.testinfra.ccm.CcmRule;
import com.datastax.oss.driver.api.testinfra.session.SessionRule;
import com.datastax.oss.driver.api.testinfra.session.SessionUtils;
import com.datastax.oss.driver.categories.ParallelizableTests;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
@Category(ParallelizableTests.class)
@CassandraRequirement(min = "2.2", description = "JSON support in Cassandra was added in 2.2")
public class JsonInsertIT {
private static final CcmRule CCM_RULE = CcmRule.getInstance();
private static final SessionRule<CqlSession> SESSION_RULE =
SessionRule.builder(CCM_RULE)
.withConfigLoader(
SessionUtils.configLoaderBuilder()
.withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(30))
.build())
.build();
@ClassRule
public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE);
private static final TypeCodec<User> JACKSON_JSON_CODEC = ExtraTypeCodecs.json(User.class);
@BeforeClass
public static void setup() {
SESSION_RULE
.session()
.execute("CREATE TABLE json_jackson_row(id int PRIMARY KEY, name text, age int)");
}
@After
public void clearTable() {
SESSION_RULE.session().execute("TRUNCATE TABLE json_jackson_row");
}
@Test
public void should_insert_string_as_json_using_simple_statement() {
// given a simple statement
try (CqlSession session = sessionWithCustomCodec()) {
String jsonUser = "{ \"id\": 2, \"name\": \"Alice\", \"age\": 3 }";
Statement stmt = insertInto("json_jackson_row").json(jsonUser).build();
// when
session.execute(stmt);
// then
String jsonUserResult =
session
.execute(selectFrom("json_jackson_row").json().all().build())
.all()
.get(0)
.getString(0);
assertThat(jsonUserResult).contains("\"id\": 2");
assertThat(jsonUserResult).contains(" \"name\": \"Alice\"");
assertThat(jsonUserResult).contains("\"age\": 3");
}
}
@Test
public void should_insert_json_using_prepare_statement() {
// given prepare statement
try (CqlSession session = sessionWithCustomCodec()) {
User user = new User(2, "bob", 35);
PreparedStatement pst =
session.prepare(insertInto("json_jackson_row").json(bindMarker("user")).build());
// when
session.execute(pst.bind().set("user", user, User.class));
// then
List<Row> rows = session.execute(selectFrom("json_jackson_row").json().all().build()).all();
assertThat(rows.get(0).get(0, User.class)).isEqualTo(user);
}
}
@Test
public void should_insert_json_using_simple_statement_with_custom_codec() {
// given a simple statement
try (CqlSession session = sessionWithCustomCodec()) {
User user = new User(1, "alice", 30);
Statement stmt = insertInto("json_jackson_row").json(user, JACKSON_JSON_CODEC).build();
// when
session.execute(stmt);
// then
List<Row> rows = session.execute(selectFrom("json_jackson_row").json().all().build()).all();
assertThat(rows.get(0).get(0, User.class)).isEqualTo(user);
}
}
@Test
public void should_insert_json_using_simple_statement_with_custom_codec_without_codec_registry() {
try (CqlSession session = sessionWithoutCustomCodec()) {
// given
User user = new User(1, "alice", 30);
SimpleStatement stmt = insertInto("json_jackson_row").json(user, JACKSON_JSON_CODEC).build();
// when
session.execute(stmt);
// then
List<Row> rows = session.execute(selectFrom("json_jackson_row").json().all().build()).all();
assertThat(rows.get(0).get(0, JACKSON_JSON_CODEC)).isEqualTo(user);
}
}
@Test
public void should_insert_json_using_simple_statement_with_codec_registry() {
// given a simple statement
try (CqlSession session = sessionWithCustomCodec()) {
User user = new User(1, "alice", 30);
Statement stmt =
insertInto("json_jackson_row")
.json(user, session.getContext().getCodecRegistry())
.build();
// when
session.execute(stmt);
// then
List<Row> rows = session.execute(selectFrom("json_jackson_row").json().all().build()).all();
assertThat(rows.get(0).get(0, User.class)).isEqualTo(user);
}
}
@Test
public void
should_throw_when_insert_json_using_simple_statement_with_codec_registry_without_custom_codec() {
assertThatThrownBy(
() -> {
try (CqlSession session = sessionWithoutCustomCodec()) {
insertInto("json_jackson_row")
.json(new User(1, "alice", 30), session.getContext().getCodecRegistry())
.build();
}
})
.isExactlyInstanceOf(IllegalArgumentException.class)
.hasMessage(
String.format(
"Could not inline JSON literal of type %s. "
+ "This happens because the provided CodecRegistry does not contain "
+ "a codec for this type. Try registering your TypeCodec in the registry "
+ "first, or use json(Object, TypeCodec).",
User.class.getName()))
.hasCauseInstanceOf(CodecNotFoundException.class);
}
@SuppressWarnings("unchecked")
private CqlSession sessionWithCustomCodec() {
return (CqlSession)
SessionUtils.baseBuilder()
.addContactEndPoints(CCM_RULE.getContactPoints())
.withKeyspace(SESSION_RULE.keyspace())
.addTypeCodecs(JACKSON_JSON_CODEC)
.build();
}
@SuppressWarnings("unchecked")
private CqlSession sessionWithoutCustomCodec() {
return (CqlSession)
SessionUtils.baseBuilder()
.addContactEndPoints(CCM_RULE.getContactPoints())
.withKeyspace(SESSION_RULE.keyspace())
.build();
}
@SuppressWarnings("unused")
public static class User {
private final int id;
private final String name;
private final int age;
@JsonCreator
public User(
@JsonProperty("id") int id,
@JsonProperty("name") String name,
@JsonProperty("age") int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return String.format("%s (id %d, age %d)", name, id, age);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other instanceof User) {
User that = (User) other;
return this.id == that.id && this.age == that.age && Objects.equals(this.name, that.name);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
}
}