diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/TableAliases.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/TableAliases.scala index 41e0b99e5..e3f06acbe 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/TableAliases.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/TableAliases.scala @@ -79,7 +79,8 @@ trait TableAliases[T <: CassandraTable[T, R], R] { self: CassandraTable[T, R] => ) extends Col[Option[RR]] { override def parse(r: Row): Try[Option[RR]] = ev.fromRow(name, r) match { case Success(value) => Success(Some(value)) - case Failure(_) => Success(None) + case Failure(_) if r.isNull(name) => Success(None) + case Failure(ex) => Failure[Option[RR]](ex) } } diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/PrimitiveMacro.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/PrimitiveMacro.scala index 90e287004..961ffb129 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/PrimitiveMacro.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/PrimitiveMacro.scala @@ -312,6 +312,8 @@ class PrimitiveMacro(override val c: blackbox.Context) extends BlackboxToolbelt } } + + def optionPrimitive(tpe: Type): Tree = { tpe.typeArgs match { case head :: Nil => q"$prefix.Primitives.option[$head]" diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/Primitives.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/Primitives.scala index 2a3cca27e..bd4132d45 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/Primitives.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/primitives/Primitives.scala @@ -213,7 +213,7 @@ object Primitives { 1, "Invalid 8-bits integer value, expecting 1 byte but got " + source.remaining() ) { - case b => source.get(source.position()) + case _ => source.get(source.position()) } } } @@ -359,7 +359,7 @@ object Primitives { override def serialize(obj: BigDecimal, version: ProtocolVersion): ByteBuffer = { obj match { case Primitive.nullValue => Primitive.nullValue - case decimal => + case _ => val bi = obj.bigDecimal.unscaledValue val bibytes = bi.toByteArray @@ -392,7 +392,7 @@ object Primitives { } object InetAddressPrimitive extends Primitive[InetAddress] { - override val dataType = CQLSyntax.Types.Inet + override val dataType: String = CQLSyntax.Types.Inet override def asCql(value: InetAddress): String = CQLQuery.empty.singleQuote(value.getHostAddress) @@ -409,14 +409,14 @@ object Primitives { InetAddress.getByAddress(Bytes.getArray(bytes)) catch { case e: UnknownHostException => - throw new InvalidTypeException("Invalid bytes for inet value, got " + bytes.remaining + " bytes") + throw new InvalidTypeException("Invalid bytes for inet value, got " + bytes.remaining + " bytes", e) } } } } object BigIntPrimitive extends Primitive[BigInt] { - override val dataType = CQLSyntax.Types.Varint + override val dataType: String = CQLSyntax.Types.Varint override def asCql(value: BigInt): String = value.toString() @@ -428,13 +428,13 @@ object Primitives { bytes match { case Primitive.nullValue => Primitive.nullValue case b if b.remaining() == 0 => Primitive.nullValue - case bt => new BigInteger(Bytes.getArray(bytes)) + case _ => new BigInteger(Bytes.getArray(bytes)) } } } object BlobIsPrimitive extends Primitive[ByteBuffer] { - override val dataType = CQLSyntax.Types.Blob + override val dataType: String = CQLSyntax.Types.Blob override def asCql(value: ByteBuffer): String = Bytes.toHexString(value) @@ -444,9 +444,9 @@ object Primitives { } object LocalDateIsPrimitive extends Primitive[LocalDate] { - override val dataType = CQLSyntax.Types.Timestamp + override val dataType: String = CQLSyntax.Types.Timestamp - val codec = IntPrimitive + val codec: IntPrimitive.type = IntPrimitive override def asCql(value: LocalDate): String = ParseUtils.quote(value.toString) @@ -461,7 +461,7 @@ object Primitives { bytes match { case Primitive.nullValue => Primitive.nullValue case b if b.remaining() == 0 => Primitive.nullValue - case b @ _ => + case _ => val unsigned = codec.deserialize(bytes, version) val signed = CodecUtils.fromUnsignedToSignedInt(unsigned) LocalDate.fromDaysSinceEpoch(signed) @@ -474,7 +474,7 @@ object Primitives { new DateTime(_, DateTimeZone.UTC) )(LongPrimitive)(CQLSyntax.Types.Timestamp) - val SqlTimestampIsPrimitive = Primitive.manuallyDerive[Timestamp, Long]( + val SqlTimestampIsPrimitive: Primitive[Timestamp] = Primitive.manuallyDerive[Timestamp, Long]( ts => ts.getTime, dt => Timestamp.from(Instant.ofEpochMilli(dt)) )(LongPrimitive)(CQLSyntax.Types.Timestamp) @@ -499,7 +499,7 @@ object Primitives { override def asCql(value: M[RR]): String = converter(value) - override val dataType = cType + override val dataType: String = cType override def serialize(coll: M[RR], version: ProtocolVersion): ByteBuffer = { coll match { @@ -553,6 +553,7 @@ object Primitives { ) } + def option[T : Primitive]: Primitive[Option[T]] = { val ev = implicitly[Primitive[T]] diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/query/InsertQuery.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/query/InsertQuery.scala index 893d0ba7d..4c87ed724 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/query/InsertQuery.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/query/InsertQuery.scala @@ -59,7 +59,7 @@ case class InsertQuery[ final def json(value: PrepareMark): InsertJsonQuery[Table, Record, Status, String :: PS] = { new InsertJsonQuery[Table, Record, Status, String :: PS]( table = table, - init = QueryBuilder.Insert.json(init, value.qb.queryString), + init = QueryBuilder.Insert.json(init, value), usingPart = usingPart, lightweightPart = lightweightPart, options = options diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/CollectionModifiers.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/CollectionModifiers.scala index dc6589eb0..f903a002d 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/CollectionModifiers.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/CollectionModifiers.scala @@ -178,10 +178,6 @@ private[builder] abstract class CollectionModifiers(queryBuilder: QueryBuilder) .forcePad.append(CQLSyntax.Symbols.`}`) } - def mapType(keyType: String, valueType: String): CQLQuery = { - diamond(CQLSyntax.Collections.map, CQLQuery(List(keyType, valueType)).queryString) - } - def mapType[K, V](key: Primitive[K], value: Primitive[V]): CQLQuery = { diamond(CQLSyntax.Collections.map, CQLQuery( List(frozen(key).queryString, frozen(value).queryString) diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/InsertQueryBuilder.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/InsertQueryBuilder.scala index 28e04dd82..d9a9bc01e 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/InsertQueryBuilder.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/builder/serializers/InsertQueryBuilder.scala @@ -16,6 +16,7 @@ package com.outworkers.phantom.builder.serializers import com.outworkers.phantom.builder.query.engine.CQLQuery +import com.outworkers.phantom.builder.query.prepared.PrepareMark import com.outworkers.phantom.builder.syntax.CQLSyntax private[phantom] trait InsertQueryBuilder { @@ -39,6 +40,16 @@ private[phantom] trait InsertQueryBuilder { init.pad.append("JSON").pad.append(CQLQuery.escape(jsonString)) } + /** + * Creates a CQL 2.2 JSON insert clause for a prepared mark. + * @param init The initialization query of the Insert clause, generally comprising the "INSERT INTO tableName" part. + * @param mark The prepared question mark. + * @return A CQL query with the JSON prefix appended to the insert. + */ + def json(init: CQLQuery, mark: PrepareMark): CQLQuery = { + init.pad.append("JSON").pad.append(mark.qb) + } + def columns(seq: Seq[CQLQuery]): CQLQuery = { CQLQuery.empty.wrapn(seq.map(_.queryString)) } diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/column/CollectionColumn.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/column/CollectionColumn.scala index d3e766182..32c5c67df 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/column/CollectionColumn.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/column/CollectionColumn.scala @@ -48,7 +48,7 @@ class CollectionColumn[ override def asCql(v: M[RR]): String = ev.asCql(v) - override val cassandraType = QueryBuilder.Collections.collectionType( + override val cassandraType: String = QueryBuilder.Collections.collectionType( collection, vp.dataType, shouldFreeze, diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/column/MapColumn.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/column/MapColumn.scala index 30160f43d..3be481146 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/column/MapColumn.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/column/MapColumn.scala @@ -29,8 +29,9 @@ private[phantom] abstract class AbstractMapColumn[ Record, K, V -](table: CassandraTable[Owner, Record]) extends Column[Owner, Record, Map[K, V]](table) - with CollectionValueDefinition[V] { +]( + table: CassandraTable[Owner, Record] +) extends Column[Owner, Record, Map[K, V]](table) with CollectionValueDefinition[V] { def keyAsCql(v: K): String diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/DbOps.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/DbOps.scala index 2d1cdfa9b..dbf38cee8 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/DbOps.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/DbOps.scala @@ -62,7 +62,7 @@ abstract class DbOps[ def createAsync()( implicit ex: ExecutionContextExecutor ): F[Seq[Seq[ResultSet]]] = { - ExecutionHelper.sequencedTraverse(tables.map(_.create.ifNotExists().delegate)) { query => + ExecutionHelper.sequencedTraverse(tables.map(_.autocreate(db.space).delegate)) { query => QueryContext.create(query) } } diff --git a/phantom-dsl/src/test/scala/com/outworkers/phantom/PhantomSuite.scala b/phantom-dsl/src/test/scala/com/outworkers/phantom/PhantomSuite.scala index 1aaf93f85..5b76ef143 100644 --- a/phantom-dsl/src/test/scala/com/outworkers/phantom/PhantomSuite.scala +++ b/phantom-dsl/src/test/scala/com/outworkers/phantom/PhantomSuite.scala @@ -19,8 +19,10 @@ import java.util.concurrent.TimeUnit import com.datastax.driver.core.VersionNumber import com.outworkers.phantom.database.DatabaseProvider +import com.outworkers.phantom.dsl.{DateTime, UUID} import com.outworkers.phantom.tables.TestDatabase import com.outworkers.util.samplers._ +import io.circe.{Encoder, Json} import org.joda.time.{DateTime, DateTimeZone, LocalDate} import org.json4s.Formats import org.scalatest._ @@ -79,6 +81,11 @@ trait TestDatabaseProvider extends DatabaseProvider[TestDatabase] { } trait PhantomSuite extends FlatSpec with PhantomBaseSuite with TestDatabaseProvider { + + + implicit val datetimeEncoder: Encoder[DateTime] = Encoder.instance(dt => Json.fromLong(dt.getMillis)) + implicit val uuidEncoder: Encoder[UUID] = Encoder.instance(uuid => Json.fromString(uuid.toString)) + def requireVersion[T](v: VersionNumber)(fn: => T): Unit = if (cassandraVersion.value.compareTo(v) >= 0) { val _ = fn } else { diff --git a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/primitives/PrimitiveRoundtripTests.scala b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/primitives/PrimitiveRoundtripTests.scala index 41e5535ae..2cc89e3b7 100644 --- a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/primitives/PrimitiveRoundtripTests.scala +++ b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/primitives/PrimitiveRoundtripTests.scala @@ -51,11 +51,11 @@ class PrimitiveRoundtripTests implicit val bigDecimalArb: Arbitrary[BigDecimal] = Sample.arbitrary[BigDecimal] - private[this] val protocol = ProtocolVersion.V5 + val protocolGen: Gen[ProtocolVersion] = Gen.alphaStr.map(_ => ProtocolVersion.V5) def roundtrip[T : Primitive](gen: Gen[T]): Assertion = { val ev = Primitive[T] - forAll(gen) { sample => + forAll(gen, protocolGen) { (sample, protocol) => ev.deserialize(ev.serialize(sample, protocol), protocol) shouldEqual sample } } @@ -65,10 +65,7 @@ class PrimitiveRoundtripTests } def roundtrip[T : Primitive : Arbitrary]: Assertion = { - val ev = Primitive[T] - forAll { sample: T => - ev.deserialize(ev.serialize(sample, protocol), protocol) shouldEqual sample - } + roundtrip(implicitly[Arbitrary[T]].arbitrary) } it should "serialize and deserialize a String primitive" in { diff --git a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/db/crud/MapOperationsTest.scala b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/db/crud/MapOperationsTest.scala index 5f70ab933..93ae3fb59 100644 --- a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/db/crud/MapOperationsTest.scala +++ b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/db/crud/MapOperationsTest.scala @@ -30,6 +30,24 @@ class MapOperationsTest extends PhantomSuite { database.scalaPrimitivesTable.createSchema() } + it should "support a single item map set operation" in { + val recipe = gen[Recipe] + val (key , value) = gen[(String, String)] + + val operation = for { + insertDone <- database.recipes.store(recipe).future() + update <- database.recipes.update.where(_.url eqs recipe.url).modify(_.props set (key, value)).future() + + select <- database.recipes.select(_.props).where(_.url eqs recipe.url).one + } yield select + + whenReady(operation) { items => + items.value.get(key) shouldBe defined + items.value.get(key).value shouldEqual value + } + } + + it should "support a single item map put operation" in { val recipe = gen[Recipe] val item = gen[(String, String)] diff --git a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/prepared/PreparedInsertQueryTest.scala b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/prepared/PreparedInsertQueryTest.scala index 1513e5cb6..41f47e392 100644 --- a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/prepared/PreparedInsertQueryTest.scala +++ b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/prepared/PreparedInsertQueryTest.scala @@ -20,6 +20,9 @@ import com.outworkers.phantom.builder.primitives.{DerivedField, DerivedTupleFiel import com.outworkers.phantom.dsl.{?, _} import com.outworkers.phantom.tables.{DerivedRecord, PrimitiveCassandra22, PrimitiveRecord, Recipe} import com.outworkers.util.samplers._ +import io.circe.generic.auto._ +import io.circe.syntax._ +import io.circe.{Encoder, Json} class PreparedInsertQueryTest extends PhantomSuite { @@ -81,6 +84,38 @@ class PreparedInsertQueryTest extends PhantomSuite { } } + + it should "execute a non-asynchronous prepared JSON insert query" in { + val sample = gen[Recipe] + + val query = database.recipes.insert.json(?).prepare() + + val chain = for { + _ <- query.bind(sample.asJson.noSpaces).future() + res <- database.recipes.select.where(_.url eqs sample.url).one() + } yield res + + whenReady(chain) { res => + res shouldBe defined + res.value shouldEqual sample + } + } + + it should "execute an asynchronous prepared JSON insert query" in { + val sample = gen[Recipe] + + val chain = for { + query <- database.recipes.insert.json(?).prepareAsync() + _ <- query.bind(sample.asJson.noSpaces).future() + res <- database.recipes.select.where(_.url eqs sample.url).one() + } yield res + + whenReady(chain) { res => + res shouldBe defined + res.value shouldEqual sample + } + } + it should "serialize a primitives insert query" in { val sample = gen[PrimitiveRecord] diff --git a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/sasi/SASIIntegrationTest.scala b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/sasi/SASIIntegrationTest.scala index d731399ff..2f674f7d0 100644 --- a/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/sasi/SASIIntegrationTest.scala +++ b/phantom-dsl/src/test/scala/com/outworkers/phantom/builder/query/sasi/SASIIntegrationTest.scala @@ -61,10 +61,10 @@ class SASIIntegrationTest extends PhantomSuite { val samples = genList[MultiSASIRecord]().map(item => item.copy(phoneNumber = pre + item.phoneNumber)) if (cassandraVersion.value >= Version.`3.4.0`) { - val ps = db.multiSasiTable.select.where(_.phoneNumber like prefix(?)).prepareAsync() val chain = for { + ps <- db.multiSasiTable.select.where(_.phoneNumber like prefix(?)).prepareAsync() _ <- db.multiSasiTable.storeRecords(samples) - query <- ps.flatMap(_.bind(PrefixValue(pre)).fetch()) + query <- ps.bind(PrefixValue(pre)).fetch() } yield query whenReady(chain) { results => @@ -95,11 +95,10 @@ class SASIIntegrationTest extends PhantomSuite { val samples = genList[MultiSASIRecord]().map(item => item.copy(name = item.name + suf)) if (cassandraVersion.value >= Version.`3.4.0`) { - - val ps = db.multiSasiTable.select.where(_.name like suffix(?)).prepareAsync() val chain = for { + ps <- db.multiSasiTable.select.where(_.name like suffix(?)).prepareAsync() _ <- db.multiSasiTable.storeRecords(samples) - query <- ps.flatMap(_.bind(SuffixValue(suf)).fetch()) + query <- ps.bind(SuffixValue(suf)).fetch() } yield query whenReady(chain) { results => @@ -180,13 +179,10 @@ class SASIIntegrationTest extends PhantomSuite { val samples = genList[MultiSASIRecord]().map(item => item.copy(customers = pre)) if (cassandraVersion.value >= Version.`3.4.0`) { - - val query = db.multiSasiTable.select.where(_.customers >= ?).prepareAsync() - val chain = for { + bindable <- db.multiSasiTable.select.where(_.customers >= ?).prepareAsync() _ <- db.multiSasiTable.truncate().future() _ <- db.multiSasiTable.storeRecords(samples) - bindable <- query query <- bindable.bind(pre).fetch() } yield query @@ -221,12 +217,12 @@ class SASIIntegrationTest extends PhantomSuite { if (cassandraVersion.value >= Version.`3.4.0`) { - val ps = db.multiSasiTable.select.where(_.customers <= ?).prepareAsync() val chain = for { + ps <- db.multiSasiTable.select.where(_.customers <= ?).prepareAsync() _ <- db.multiSasiTable.truncate().future() _ <- db.multiSasiTable.storeRecords(samples) - query <- ps.flatMap(_.bind(pre).fetch()) + query <- ps.bind(pre).fetch() } yield query whenReady(chain) { results => @@ -295,12 +291,12 @@ class SASIIntegrationTest extends PhantomSuite { val samples = genList[MultiSASIRecord]().map(item => item.copy(customers = pre)) if (cassandraVersion.value >= Version.`3.4.0`) { - val ps = db.multiSasiTable.select.where(_.customers > ?).prepareAsync() val chain = for { + ps <- db.multiSasiTable.select.where(_.customers > ?).prepareAsync() _ <- db.multiSasiTable.truncate().future() _ <- db.multiSasiTable.storeRecords(samples) - query <- ps.flatMap(_.bind(pre).fetch()) + query <- ps.bind(pre).fetch() } yield query whenReady(chain) { results => diff --git a/phantom-example/src/main/scala/com/outworkers/phantom/example/basics/ThriftModels.scala b/phantom-example/src/main/scala/com/outworkers/phantom/example/basics/ThriftModels.scala index 1f82e0b58..b7127b9fd 100644 --- a/phantom-example/src/main/scala/com/outworkers/phantom/example/basics/ThriftModels.scala +++ b/phantom-example/src/main/scala/com/outworkers/phantom/example/basics/ThriftModels.scala @@ -16,7 +16,7 @@ package com.outworkers.phantom.example.basics import com.outworkers.phantom.dsl._ -import com.outworkers.phantom.thrift._ +import com.outworkers.phantom.thrift.compact._ import com.outworkers.phantom.example.basics.thrift.SampleModel // Sample model here comes from the Thrift struct definition. @@ -34,5 +34,5 @@ abstract class ThriftTable extends Table[ThriftTable, SampleRecord] { // By default, com.outworkers.phantom will use a compact Thrift serializer. // And store the records as strings in Cassandra. - object thriftModel extends ThriftColumn[ThriftTable, SampleRecord, SampleModel](this) + object thriftModel extends Col[SampleModel] } diff --git a/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/TwitterFutures.scala b/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/TwitterFutures.scala index 5e3fe8a8a..edc8cae6a 100644 --- a/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/TwitterFutures.scala +++ b/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/TwitterFutures.scala @@ -31,5 +31,4 @@ trait TwitterFutures extends Waiters with ScalaFutures { override def isCanceled: Boolean = false } - } diff --git a/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/query/prepared/PreparedInsertQueryTest.scala b/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/query/prepared/PreparedInsertQueryTest.scala index eabf871f7..f58978d7e 100644 --- a/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/query/prepared/PreparedInsertQueryTest.scala +++ b/phantom-finagle/src/test/scala/com/outworkers/phantom/finagle/query/prepared/PreparedInsertQueryTest.scala @@ -99,7 +99,7 @@ class PreparedInsertQueryTest extends PhantomSuite with TwitterFutures { .prepare() val chain = for { - store <- query.bind(sample).future() + _ <- query.bind(sample).future() get <- database.primitives.select.where(_.pkey eqs sample.pkey).one() } yield get diff --git a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/ThriftHelper.scala b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/ThriftHelper.scala index 4d2883cea..ca4a5a219 100644 --- a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/ThriftHelper.scala +++ b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/ThriftHelper.scala @@ -15,17 +15,27 @@ */ package com.outworkers.phantom.thrift -import com.twitter.scrooge.CompactThriftSerializer +import com.twitter.scrooge.ThriftStructSerializer + import scala.reflect.macros.blackbox -trait ThriftHelper[ValueType <: ThriftStruct] { - def serializer: CompactThriftSerializer[ValueType] +trait ThriftHelper[ + ValueType <: ThriftStruct, + Serializer <: ThriftStructSerializer[ValueType] +] { + def serializer: Serializer } object ThriftHelper { - def apply[T <: ThriftStruct](implicit ev: ThriftHelper[T]): ThriftHelper[T] = ev + def apply[ + T <: ThriftStruct, + Serializer <: ThriftStructSerializer[T] + ](implicit ev: ThriftHelper[T, Serializer]): ThriftHelper[T, Serializer] = ev - implicit def materializer[T <: ThriftStruct]: ThriftHelper[T] = macro ThriftHelperMacro.materialize[T] + implicit def materializer[ + T <: ThriftStruct, + Serializer <: ThriftStructSerializer[T] + ]: ThriftHelper[T, Serializer] = macro ThriftHelperMacro.materialize[T, Serializer] } @macrocompat.bundle @@ -35,16 +45,21 @@ class ThriftHelperMacro(val c: blackbox.Context) { private[this] val pkgRoot = q"_root_.com.outworkers.phantom.thrift" private[this] val scroogePkg = q"_root_.com.twitter.scrooge" - private[this] val serializerTpe: Type => Tree = t => tq"$scroogePkg.CompactThriftSerializer[$t]" - def materialize[T <: ThriftStruct : WeakTypeTag]: Tree = { + def materialize[ + T <: ThriftStruct : WeakTypeTag, + Serializer : WeakTypeTag + ]: Tree = { val tpe = weakTypeOf[T] - val companion = tpe.typeSymbol.companion + val sTpe = weakTypeOf[Serializer] + //sTpe.typeConstructor + val valueCompanion = tpe.typeSymbol.companion + val serializerCompanion = sTpe.typeSymbol.companion q""" - new $pkgRoot.ThriftHelper[$tpe] { - override val serializer: ${serializerTpe(tpe)} = new $scroogePkg.CompactThriftSerializer[$tpe] { - override val codec: $scroogePkg.ThriftStructCodec[$tpe] = $companion + new $pkgRoot.ThriftHelper[$tpe, $sTpe] { + override val serializer: $sTpe = { + $scroogePkg.$serializerCompanion.apply[$tpe]($valueCompanion) } } """ diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/TwitterFutures.scala b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/Ops.scala similarity index 51% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/TwitterFutures.scala rename to phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/Ops.scala index 8e4429762..d7e37b7c2 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/TwitterFutures.scala +++ b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/Ops.scala @@ -13,23 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.columns -import com.twitter.util.{Future, Return, Throw} -import org.scalatest.concurrent.{ScalaFutures, Waiters} +import com.outworkers.phantom.builder.primitives.Primitive +import com.outworkers.phantom.thrift.ThriftHelper +import com.twitter.scrooge.{ThriftStruct, ThriftStructSerializer} -trait TwitterFutures extends Waiters with ScalaFutures { +trait Ops[Serializer[X <: ThriftStruct] <: ThriftStructSerializer[X]] { - implicit def twitterFutureToConcept[T](f: Future[T]): FutureConcept[T] = new FutureConcept[T] { - override def eitherValue: Option[Either[Throwable, T]] = f.poll match { - case Some(Return(ret)) => Some(Right(ret)) - case Some(Throw(err)) => Some(Left(err)) - case None => None - } + type ThriftStruct = com.twitter.scrooge.ThriftStruct - override def isExpired: Boolean = false - - override def isCanceled: Boolean = false + implicit def thriftPrimitive[ + T <: ThriftStruct + ](implicit hp: ThriftHelper[T, Serializer[T]]): Primitive[T] = { + Primitive.derive[T, String](hp.serializer.toString)(hp.serializer.fromString) } - } diff --git a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/ThriftColumn.scala b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/ThriftColumn.scala deleted file mode 100644 index 6c06c13ea..000000000 --- a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/columns/ThriftColumn.scala +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2013 - 2017 Outworkers Ltd. - * - * 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.outworkers.phantom.thrift.columns - -import com.outworkers.phantom.{CassandraTable, Row} -import com.outworkers.phantom.builder.QueryBuilder -import com.outworkers.phantom.builder.primitives.Primitive -import com.outworkers.phantom.builder.primitives.Primitives.StringPrimitive -import com.outworkers.phantom.builder.query.engine.CQLQuery -import com.outworkers.phantom.builder.syntax.CQLSyntax -import com.outworkers.phantom.column._ -import com.outworkers.phantom.thrift.ThriftHelper -import com.twitter.scrooge.{CompactThriftSerializer, ThriftStruct} - -import scala.annotation.implicitNotFound -import scala.util.{Success, Try} - -sealed trait ThriftCol[ValueType <: ThriftStruct] { - - /** - * The Thrift serializer to use. - * This must always be overriden before usage. - * It is a simple forwarding from a concrete Thrift Struct. - */ - def serializer: CompactThriftSerializer[ValueType] - - /** - * This converts a value to the appropiate Cassandra type. - * All Thrift structs are serialized to strings. - * - * @param v The Thrift struct to convert. - * @return A string containing the compact Thrift serialization. - */ - def asCql(v: ValueType): String = { - CQLQuery.empty.singleQuote(serializer.toString(v)) - } - - def valueAsCql(v: ValueType): String = asCql(v) - - val primitive = implicitly[Primitive[String]] -} - -abstract class ThriftColumn[ - T <: CassandraTable[T, R], - R, - V <: ThriftStruct -](table: CassandraTable[T, R])( - implicit hp: ThriftHelper[V] -) extends Column[T, R, V](table) with ThriftCol[V] { - - val cassandraType = CQLSyntax.Types.Text - - override val serializer: CompactThriftSerializer[V] = hp.serializer - - def parse(r: Row): Try[V] = { - Try(StringPrimitive.deserialize(r.getBytesUnsafe(name), r.version)) map serializer.fromString - } -} - -abstract class OptionalThriftColumn[ - T <: CassandraTable[T, R], - R, - V <: ThriftStruct -](table: CassandraTable[T, R])( - implicit hp: ThriftHelper[V] -) extends OptionalColumn[T, R, V](table) with ThriftCol[V] { - - override val serializer: CompactThriftSerializer[V] = hp.serializer - - val cassandraType = CQLSyntax.Types.Text - - def asCql(v: Option[V]): String = { - v.map(item => CQLQuery.empty.singleQuote(serializer.toString(item))).orNull - } - - def optional(r: Row): Try[V] = { - Try(StringPrimitive.deserialize(r.getBytesUnsafe(name), r.version)) map serializer.fromString - } - -} - -class ThriftSetColumn[ - T <: CassandraTable[T, R], - R, - V <: ThriftStruct -](table: CassandraTable[T, R])( - implicit hp: ThriftHelper[V], - ev1: Primitive[String], - ev: Primitive[Set[String]] -) extends AbstractColColumn[T, R, Set, V](table) with ThriftCol[V] { - - override def valueAsCql(v: V): String = ev1.asCql(serializer.toString(v)) - - override val serializer: CompactThriftSerializer[V] = hp.serializer - - override val cassandraType = QueryBuilder.Collections.setType(CQLSyntax.Types.Text).queryString - - override def asCql(v: Set[V]): String = ev.asCql(v map serializer.toString) - - override def parse(r: Row): Try[Set[V]] = { - if (r.isNull(name)) { - Success(Set.empty[V]) - } else { - Try(ev.deserialize(r.getBytesUnsafe(name), r.version) map serializer.fromString) - } - } -} - - -class ThriftListColumn[ - T <: CassandraTable[T, R], - R, - V <: ThriftStruct -](table: CassandraTable[T, R])( - implicit hp: ThriftHelper[V], - ev: Primitive[List[String]] -) extends AbstractColColumn[T, R, List, V](table) with ThriftCol[V] { - - override def valueAsCql(v: V): String = { - CQLQuery.empty.singleQuote(serializer.toString(v)) - } - - override val cassandraType = QueryBuilder.Collections.listType(CQLSyntax.Types.Text).queryString - - override val serializer: CompactThriftSerializer[V] = hp.serializer - - override def parse(r: Row): Try[List[V]] = { - if (r.isNull(name)) { - Success(Nil) - } else { - Success(ev.deserialize(r.getBytesUnsafe(name), r.version).map(serializer.fromString)) - } - } - - /** - * Provides the serialisation mechanism of a value to a CQL string. - * The vast majority of serializers are fed in via the Primitives mechanism. - * - * Primitive columns will automatically override and define "asCql" based on the - * serialization of specific primitives. When T is context bounded by a primitive: - * - * {{{ - * def asCql(v: T): String = implicitly[Primitive[T]].asCql(value) - * }}} - * - * @param v The value of the object to convert to a string. - * @return A string that can be directly appended to a CQL query. - */ - override def asCql(v: List[V]): String = ev.asCql(v map valueAsCql) -} - -@implicitNotFound(msg = "Type ${KeyType} must be a Cassandra primitive") -class ThriftMapColumn[ - T <: CassandraTable[T, R], - R, - KeyType : Primitive, - V <: ThriftStruct -](table: CassandraTable[T, R])( - implicit hp: ThriftHelper[V], - val keyPrimitive: Primitive[KeyType], - ev: Primitive[Map[KeyType, String]] -) extends AbstractMapColumn[T, R, KeyType, V](table) with ThriftCol[V] { - - override val serializer: CompactThriftSerializer[V] = hp.serializer - - override val cassandraType = QueryBuilder.Collections.mapType( - keyPrimitive.dataType, - CQLSyntax.Types.Text - ).queryString - - override def keyAsCql(v: KeyType): String = keyPrimitive.asCql(v) - - override def parse(r: Row): Try[Map[KeyType, V]] = { - if (r.isNull(name)) { - Success(Map.empty[KeyType, V]) - } else { - Try(ev.deserialize(r.getBytesUnsafe(name), r.version).mapValues(serializer.fromString)) - } - } -} \ No newline at end of file diff --git a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/package.scala b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/package.scala index e3c9fe379..4142d547e 100644 --- a/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/package.scala +++ b/phantom-thrift/src/main/scala/com/outworkers/phantom/thrift/package.scala @@ -15,53 +15,17 @@ */ package com.outworkers.phantom -import com.outworkers.phantom.builder.primitives.Primitive +import com.outworkers.phantom.thrift.columns.Ops +import com.twitter.scrooge._ package object thrift { - type ThriftStruct = com.twitter.scrooge.ThriftStruct - - type ThriftColumn[ - T <: CassandraTable[T, R], - R, - Model <: ThriftStruct - ] = columns.ThriftColumn[T, R, Model] - - type ThriftSetColumn[ - T <: CassandraTable[T, R], - R, - Model <: ThriftStruct - ] = columns.ThriftSetColumn[T, R, Model] - - type ThriftListColumn[ - T <: CassandraTable[T, R], - R, - Model <: ThriftStruct - ] = columns.ThriftListColumn[T, R, Model] - type ThriftMapColumn[ - T <: CassandraTable[T, R], - R, - KeyType, - Model <: ThriftStruct - ] = columns.ThriftMapColumn[T, R, KeyType, Model] - - type OptionalThriftColumn[ - T <: CassandraTable[T, R], - R, - Model <: ThriftStruct - ] = columns.OptionalThriftColumn[T, R, Model] - - implicit def thriftPrimitive[T <: ThriftStruct]()(implicit hp: ThriftHelper[T]): Primitive[T] = { - val sz = hp.serializer - Primitive.derive[T, String](sz.toString)(sz.fromString) - } + type ThriftStruct = com.twitter.scrooge.ThriftStruct - implicit class PrimitiveCompanionHelper(val obj: Primitive.type) extends AnyVal { - def thrift[T <: ThriftStruct]()(implicit hp: ThriftHelper[T]): Primitive[T] = { - val sz = hp.serializer - Primitive.derive[T, String](sz.toString)(sz.fromString) - } - } + object binary extends Ops[BinaryThriftStructSerializer] + object lazybinary extends Ops[LazyBinaryThriftStructSerializer] + object tjson extends Ops[TJSONProtocolThriftSerializer] + object compact extends Ops[CompactThriftSerializer] } diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/ThriftRecord.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/ThriftRecord.scala new file mode 100644 index 000000000..1cc4c4e2a --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/ThriftRecord.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson + */ +package com.outworkers.phantom.thrift.tests + +import java.util.UUID + +import com.outworkers.phantom.thrift.models.ThriftTest + +case class ThriftRecord( + id: UUID, + name: String, + struct: ThriftTest, + thriftSet: Set[ThriftTest], + thriftList: List[ThriftTest], + thriftMap: Map[String, ThriftTest], + optThrift: Option[ThriftTest] +) \ No newline at end of file diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/BinarySuite.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/BinarySuite.scala new file mode 100644 index 000000000..e75139ad7 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/BinarySuite.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary + +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.binary +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import org.scalatest.FlatSpec + +trait BinarySuite extends FlatSpec with ThriftTestSuite { + val thriftDb = binary.ThriftDatabase + + override def beforeAll(): Unit = { + super.beforeAll() + thriftDb.create() + } +} + diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/OptionalThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/OptionalThriftColumnTest.scala similarity index 75% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/OptionalThriftColumnTest.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/OptionalThriftColumnTest.scala index 80d44bcdb..c7d2afd22 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/OptionalThriftColumnTest.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/OptionalThriftColumnTest.scala @@ -13,16 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.binary.suites import com.datastax.driver.core.utils.UUIDs import com.outworkers.phantom.dsl._ -import com.outworkers.phantom.tables.ThriftDatabase +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.phantom.thrift.tests.compact.CompactSuite import com.outworkers.util.samplers._ -import org.scalatest.FlatSpec -import org.scalatest.time.SpanSugar._ -class OptionalThriftColumnTest extends FlatSpec with ThriftTestSuite with TwitterFutures { +class OptionalThriftColumnTest extends BinarySuite { it should "find an item if it was defined" in { @@ -30,7 +29,7 @@ class OptionalThriftColumnTest extends FlatSpec with ThriftTestSuite with Twitte val sample = gen[ThriftTest] - val insert = ThriftDatabase.thriftColumnTable.insert + val insert = thriftDb.thriftColumnTable.insert .value(_.id, id) .value(_.name, sample.name) .value(_.ref, sample) @@ -41,7 +40,7 @@ class OptionalThriftColumnTest extends FlatSpec with ThriftTestSuite with Twitte val operation = for { insertDone <- insert - select <- ThriftDatabase.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one } yield select whenReady(operation) { res => @@ -54,7 +53,7 @@ class OptionalThriftColumnTest extends FlatSpec with ThriftTestSuite with Twitte val sample = gen[ThriftTest] - val insert = ThriftDatabase.thriftColumnTable.insert + val insert = thriftDb.thriftColumnTable.insert .value(_.id, id) .value(_.name, sample.name) .value(_.ref, sample) @@ -65,7 +64,7 @@ class OptionalThriftColumnTest extends FlatSpec with ThriftTestSuite with Twitte val operation = for { insertDone <- insert - select <- ThriftDatabase.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one } yield select whenReady(operation) { res => diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftBinarySerializationTests.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftBinarySerializationTests.scala new file mode 100644 index 000000000..10dcab697 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftBinarySerializationTests.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary.suites + +import com.datastax.driver.core.ProtocolVersion +import com.outworkers.phantom.builder.primitives.Primitive +import com.outworkers.phantom.thrift.binary._ +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers.Sample +import org.scalacheck.Gen +import org.scalatest.prop.GeneratorDrivenPropertyChecks +import org.scalatest.{Assertion, FlatSpec} + +class ThriftBinarySerializationTests extends FlatSpec with ThriftTestSuite with GeneratorDrivenPropertyChecks { + + val protocolGen: Gen[ProtocolVersion] = Gen.alphaChar.map(_ => ProtocolVersion.V5) + + def roundtrip[T : Primitive](gen: Gen[T]): Assertion = { + val ev = Primitive[T] + forAll(gen, protocolGen) { (sample, protocol) => + ev.deserialize(ev.serialize(sample, protocol), protocol) shouldEqual sample + } + } + + def sroundtrip[T : Primitive : Sample]: Assertion = { + roundtrip[T](Sample.arbitrary[T].arbitrary) + } + + it should "serialize binary thrift primitives" in { + sroundtrip[ThriftTest] + } + + it should "serialize lists of binary thrift primitives" in { + sroundtrip[List[ThriftTest]] + } + + it should "serialize sets of binary thrift primitives" in { + sroundtrip[Set[ThriftTest]] + } + + it should "serialize maps of binary thrift primitives with thrift values" in { + sroundtrip[Map[String, ThriftTest]] + } + + + it should "serialize maps of binary thrift primitives with thrift keys" in { + sroundtrip[Map[ThriftTest, String]] + } + + it should "serialize maps of binary thrift primitives with thrift keys and values" in { + sroundtrip[Map[ThriftTest, ThriftTest]] + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftColumnTest.scala new file mode 100644 index 000000000..d91bcec13 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftColumnTest.scala @@ -0,0 +1,63 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary.suites + +import com.datastax.driver.core.utils.UUIDs +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.util.samplers._ + +class ThriftColumnTest extends BinarySuite { + + it should "allow storing thrift columns" in { + val id = UUIDs.timeBased() + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .future() flatMap { + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() + } + + whenReady(insert) { result => + result.value.struct shouldEqual sample + } + } + + it should "allow storing lists of thrift objects" in { + val id = UUIDs.timeBased() + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sampleList = Set(sample, sample2) + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, sampleList) + .future() flatMap { + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() + } + + whenReady(insert) { result => + result.value.struct shouldEqual sample + result.value.thriftSet shouldEqual sampleList + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftIndexTableTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftIndexTableTest.scala similarity index 70% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftIndexTableTest.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftIndexTableTest.scala index c3a73f91e..330e75da8 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftIndexTableTest.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftIndexTableTest.scala @@ -13,28 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.binary.suites -import com.outworkers.phantom.tables.{ThriftDatabase, ThriftRecord} -import com.outworkers.util.samplers._ -import com.outworkers.util.testing.twitter._ import com.outworkers.phantom.finagle._ -import com.outworkers.phantom.thrift._ -import com.outworkers.phantom.thrift.models.ThriftTest -import org.scalatest.FlatSpec - -class ThriftIndexTableTest extends FlatSpec with ThriftTestSuite with TwitterFutures { - - val ThriftIndexedTable = ThriftDatabase.thriftIndexedTable +import com.outworkers.phantom.thrift.binary._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.util.samplers._ - implicit val samplePrimitive = Primitive.thrift[ThriftTest] +class ThriftIndexTableTest extends BinarySuite { it should "allow storing a thrift class inside a table indexed by a thrift struct" in { val sample = gen[ThriftRecord] val chain = for { - store <- ThriftIndexedTable.store(sample).future() - get <- ThriftIndexedTable.select.where(_.ref eqs sample.struct).one() + _ <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() } yield get whenReady(chain) { res => @@ -54,11 +49,11 @@ class ThriftIndexTableTest extends FlatSpec with ThriftTestSuite with TwitterFut val sample = gen[ThriftRecord] val chain = for { - store <- ThriftIndexedTable.store(sample).future() - get <- ThriftIndexedTable.select.where(_.ref eqs sample.struct).one() + store <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() } yield get - whenReady(chain.asScala) { res => + whenReady(chain) { res => res.value.id shouldEqual sample.id res.value.name shouldEqual sample.name res.value.struct shouldEqual sample.struct diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftListOperations.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftListOperations.scala new file mode 100644 index 000000000..4e58f2bd1 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftListOperations.scala @@ -0,0 +1,343 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary.suites + +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.util.samplers._ +import com.outworkers.util.testing.twitter._ + +class ThriftListOperations extends BinarySuite { + + it should "prepend an item to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList prepend sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample2 :: sample.thriftList) + } + } + + it should "prepend an item to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList prepend sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample2 :: sample.thriftList) + } + } + + it should "prepend several items to a thrift list column" in { + val sample = gen[ThriftRecord] + + val appendable = genList[ThriftTest]() + + val prependedValues = if (cassandraVersion.value < Version.`2.0.13`) appendable.reverse else appendable + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList prepend appendable) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual prependedValues ::: sample.thriftList + } + } + + it should "prepend several items to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val appendable = genList[ThriftTest]() + + val prependedValues = if (cassandraVersion.value < Version.`2.0.13`) appendable.reverse else appendable + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList prepend appendable) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual prependedValues ::: sample.thriftList + } + } + + it should "append an item to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual sample.thriftList :+ sample2 + } + } + + it should "append an item to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual sample.thriftList :+ sample2 + } + } + + it should "append several items to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList ::: sample2) + } + } + + it should "append several items to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList ::: sample2) + } + } + + it should "remove an item from a thrift list column" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + _ <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList discard sample2) + .future() + select <- thriftDb.thriftColumnTable + .select(_.thriftList) + .where(_.id eqs sample.id) + .one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff List(sample2)) + } + } + + it should "remove an item from a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + _ <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList discard sample2) + .future() + select <- thriftDb.thriftColumnTable + .select(_.thriftList) + .where(_.id eqs sample.id) + .one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff List(sample2)) + } + } + + it should "remove several items from a thrift list column" in { + val sample = gen[ThriftRecord] + + val removables = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id) + .modify(_.thriftList discard removables) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff removables) + } + } + + it should "remove several items from a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val removables = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id) + .modify(_.thriftList discard removables) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff removables) + } + } + + it should "set an index to a given value" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList setIdx(0, sample2)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.isDefinedAt(2) shouldEqual true + items.value should contain (sample2) + } + } + + it should "set an index to a given value with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList setIdx(0, sample2)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } + + it should "set a non-zero index to a given value" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftList setIdx(2, sample2)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } + + it should "set a non-zero index to a given value with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftList setIdx(2, sample2)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftMapColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftMapColumnTest.scala new file mode 100644 index 000000000..0bb1991be --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftMapColumnTest.scala @@ -0,0 +1,105 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary.suites + +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.util.samplers._ + +class ThriftMapColumnTest extends BinarySuite { + + it should "put an item to a thrift map column" in { + val sample = gen[ThriftRecord] + val toAdd = gen[(String, ThriftTest)] + val expected = sample.thriftMap + toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftMap put toAdd) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldEqual expected + } + } + + it should "put an item to a thrift map column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val toAdd = gen[(String, ThriftTest)] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftMap put toAdd).future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftMap + toAdd) + } + } + + + it should "put several items to a thrift map column" in { + val sample = gen[ThriftRecord] + + val toAdd = genMap[String, ThriftTest]() + val expected = sample.thriftMap ++ toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftMap putAll toAdd).future() + select <- thriftDb.thriftColumnTable + .select(_.thriftMap) + .where(_.id eqs sample.id) + .one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.size shouldEqual expected.size + items.value shouldEqual expected + } + } + + it should "put several items to a thrift map column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val toAdd = genMap[String, ThriftTest]() + val expected = sample.thriftMap ++ toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftMap putAll toAdd).future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.size shouldEqual expected.size + items.value shouldEqual expected + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftSetOperationsTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftSetOperationsTest.scala new file mode 100644 index 000000000..d02b05bdd --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/suites/ThriftSetOperationsTest.scala @@ -0,0 +1,129 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.binary.suites + +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.binary.BinarySuite +import com.outworkers.util.samplers._ + +class ThriftSetOperationsTest extends BinarySuite { + + it should "add an item to a thrift set column" in { + + val id = gen[UUID] + + val sample = gen[ThriftTest] + + val sample2 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet add sample2).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldBe Set(sample, sample2) + } + } + + it should "add several items a thrift set column" in { + + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet addAll Set(sample2, sample3)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample, sample2, sample3) + } + } + + it should "remove one item from a thrift set column" in { + + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample, sample2, sample3)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet remove sample3).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample, sample2) + } + } + + it should "remove several items from thrift set column" in { + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample, sample2, sample3)) + + val operation = for { + insertDone <- insert.future() + update <- thriftDb + .thriftColumnTable.update.where(_.id eqs id) + .modify(_.thriftSet removeAll Set(sample2, sample3)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample) + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/tables/ThriftColumnTable.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/tables.scala similarity index 50% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/tables/ThriftColumnTable.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/tables.scala index ff42a4516..ec12752cc 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/tables/ThriftColumnTable.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/binary/tables.scala @@ -13,37 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.tables +package com.outworkers.phantom.thrift.tests.binary -import java.util.UUID import com.outworkers.phantom.connectors.CassandraConnection import com.outworkers.phantom.database.Database import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.tables.Connector import com.outworkers.phantom.thrift.models._ -import com.outworkers.phantom.thrift._ - -case class ThriftRecord( - id: UUID, - name: String, - struct: ThriftTest, - thriftSet: Set[ThriftTest], - thriftList: List[ThriftTest], - thriftMap: Map[String, ThriftTest], - optThrift: Option[ThriftTest] -) +import com.outworkers.phantom.thrift.binary._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.compact.ThriftDatabase abstract class ThriftColumnTable extends Table[ThriftColumnTable, ThriftRecord] { object id extends UUIDColumn with PartitionKey object name extends StringColumn - object ref extends ThriftColumn[ThriftColumnTable, ThriftRecord, ThriftTest](this) + object ref extends Col[ThriftTest] - object thriftSet extends ThriftSetColumn[ThriftColumnTable, ThriftRecord, ThriftTest](this) + object thriftSet extends SetColumn[ThriftTest] - object thriftList extends ThriftListColumn[ThriftColumnTable, ThriftRecord, ThriftTest](this) + object thriftList extends ListColumn[ThriftTest] - object thriftMap extends ThriftMapColumn[ThriftColumnTable, ThriftRecord, String, ThriftTest](this) + object thriftMap extends MapColumn[String, ThriftTest] - object optionalThrift extends OptionalThriftColumn[ThriftColumnTable, ThriftRecord, ThriftTest](this) + object optionalThrift extends OptionalCol[ThriftTest] } abstract class ThriftIndexedTable extends Table[ThriftIndexedTable, ThriftRecord] { @@ -51,18 +43,20 @@ abstract class ThriftIndexedTable extends Table[ThriftIndexedTable, ThriftRecord object id extends UUIDColumn object name extends StringColumn - object ref extends ThriftColumn[ThriftIndexedTable, ThriftRecord, ThriftTest](this) with PartitionKey + object ref extends Col[ThriftTest] with PartitionKey - object thriftSet extends ThriftSetColumn[ThriftIndexedTable, ThriftRecord, ThriftTest](this) + object thriftSet extends SetColumn[ThriftTest] - object thriftList extends ThriftListColumn[ThriftIndexedTable, ThriftRecord, ThriftTest](this) + object thriftList extends ListColumn[ThriftTest] - object thriftMap extends ThriftMapColumn[ThriftIndexedTable, ThriftRecord, String, ThriftTest](this) + object thriftMap extends MapColumn[String, ThriftTest] - object optionalThrift extends OptionalThriftColumn[ThriftIndexedTable, ThriftRecord, ThriftTest](this) + object optionalThrift extends OptionalCol[ThriftTest] } -class ThriftDatabase(override val connector: CassandraConnection) extends Database[ThriftDatabase](connector) { +abstract class ThriftDatabase( + override val connector: CassandraConnection +) extends Database[ThriftDatabase](connector) { object thriftColumnTable extends ThriftColumnTable with Connector object thriftIndexedTable extends ThriftIndexedTable with Connector } diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/CompactSuite.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/CompactSuite.scala new file mode 100644 index 000000000..1124f40f9 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/CompactSuite.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact + +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.compact +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import org.scalatest.FlatSpec + +trait CompactSuite extends FlatSpec with ThriftTestSuite { + val thriftDb = compact.ThriftDatabase + + override def beforeAll(): Unit = { + super.beforeAll() + thriftDb.create() + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/OptionalThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/OptionalThriftColumnTest.scala new file mode 100644 index 000000000..f33a85604 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/OptionalThriftColumnTest.scala @@ -0,0 +1,73 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact.suites + +import com.datastax.driver.core.utils.UUIDs +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.util.samplers._ + +class OptionalThriftColumnTest extends CompactSuite { + + it should "find an item if it was defined" in { + + val id = UUIDs.timeBased() + + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .value(_.thriftList, List(sample)) + .value(_.optionalThrift, Some(sample)) + .future() + + val operation = for { + insertDone <- insert + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + } yield select + + whenReady(operation) { res => + res.value shouldBe Some(sample) + } + } + + it should "not find an item if was not defined" in { + val id = UUIDs.timeBased() + + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .value(_.thriftList, List(sample)) + .value(_.optionalThrift, None) + .future() + + val operation = for { + insertDone <- insert + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + } yield select + + whenReady(operation) { res => + res.value.isDefined shouldBe false + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThirftCompactSerializationTests.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThirftCompactSerializationTests.scala new file mode 100644 index 000000000..fefa4beb8 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThirftCompactSerializationTests.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact.suites + +import com.datastax.driver.core.ProtocolVersion +import com.outworkers.phantom.builder.primitives.Primitive +import com.outworkers.phantom.thrift.compact._ +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers.Sample +import org.scalacheck.Gen +import org.scalatest.prop.GeneratorDrivenPropertyChecks +import org.scalatest.{Assertion, FlatSpec} + +class ThirftCompactSerializationTests extends FlatSpec with ThriftTestSuite with GeneratorDrivenPropertyChecks { + + val protocolGen: Gen[ProtocolVersion] = Gen.alphaChar.map(_ => ProtocolVersion.V5) + + def roundtrip[T : Primitive](gen: Gen[T]): Assertion = { + val ev = Primitive[T] + forAll(gen, protocolGen) { (sample, protocol) => + ev.deserialize(ev.serialize(sample, protocol), protocol) shouldEqual sample + } + } + + def sroundtrip[T : Primitive : Sample]: Assertion = { + roundtrip[T](Sample.arbitrary[T].arbitrary) + } + + it should "serialize binary thrift primitives" in { + sroundtrip[ThriftTest] + } + + it should "serialize lists of binary thrift primitives" in { + sroundtrip[List[ThriftTest]] + } + + it should "serialize sets of binary thrift primitives" in { + sroundtrip[Set[ThriftTest]] + } + + it should "serialize maps of binary thrift primitives with thrift values" in { + sroundtrip[Map[String, ThriftTest]] + } + + + it should "serialize maps of binary thrift primitives with thrift keys" in { + sroundtrip[Map[ThriftTest, String]] + } + + it should "serialize maps of binary thrift primitives with thrift keys and values" in { + sroundtrip[Map[ThriftTest, ThriftTest]] + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftColumnTest.scala similarity index 77% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftColumnTest.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftColumnTest.scala index c8de25d0d..6d3923909 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftColumnTest.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftColumnTest.scala @@ -13,26 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.compact.suites import com.datastax.driver.core.utils.UUIDs -import com.outworkers.phantom.tables.ThriftDatabase import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.compact.CompactSuite import com.outworkers.util.samplers._ -import org.scalatest.FlatSpec -class ThriftColumnTest extends FlatSpec with ThriftTestSuite { +class ThriftColumnTest extends CompactSuite { it should "allow storing thrift columns" in { val id = UUIDs.timeBased() val sample = gen[ThriftTest] - val insert = ThriftDatabase.thriftColumnTable.insert + val insert = thriftDb.thriftColumnTable.insert .value(_.id, id) .value(_.name, sample.name) .value(_.ref, sample) .future() flatMap { - _ => ThriftDatabase.thriftColumnTable.select.where(_.id eqs id).one() + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() } whenReady(insert) { result => @@ -46,13 +45,13 @@ class ThriftColumnTest extends FlatSpec with ThriftTestSuite { val sample2 = gen[ThriftTest] val sampleList = Set(sample, sample2) - val insert = ThriftDatabase.thriftColumnTable.insert + val insert = thriftDb.thriftColumnTable.insert .value(_.id, id) .value(_.name, sample.name) .value(_.ref, sample) .value(_.thriftSet, sampleList) .future() flatMap { - _ => ThriftDatabase.thriftColumnTable.select.where(_.id eqs id).one() + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() } whenReady(insert) { result => diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftIndexTableTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftIndexTableTest.scala new file mode 100644 index 000000000..90e2ee576 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftIndexTableTest.scala @@ -0,0 +1,67 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact.suites + +import com.outworkers.util.samplers._ +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.compact._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.compact.CompactSuite + +class ThriftIndexTableTest extends CompactSuite { + + it should "allow storing a thrift class inside a table indexed by a thrift struct" in { + val sample = gen[ThriftRecord] + + val chain = for { + _ <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() + } yield get + + whenReady(chain) { res => + res.value.id shouldEqual sample.id + res.value.name shouldEqual sample.name + res.value.struct shouldEqual sample.struct + res.value.optThrift shouldEqual sample.optThrift + res.value.thriftList shouldEqual sample.thriftList + res.value.thriftMap shouldEqual sample.thriftMap + res.value.thriftSet shouldEqual sample.thriftSet + + res.value shouldEqual sample + } + } + + it should "allow storing a thrift class inside a table indexed by a thrift struct with Twitter futures" in { + val sample = gen[ThriftRecord] + + val chain = for { + store <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() + } yield get + + whenReady(chain) { res => + res.value.id shouldEqual sample.id + res.value.name shouldEqual sample.name + res.value.struct shouldEqual sample.struct + res.value.optThrift shouldEqual sample.optThrift + res.value.thriftList shouldEqual sample.thriftList + res.value.thriftMap shouldEqual sample.thriftMap + res.value.thriftSet shouldEqual sample.thriftSet + + res.value shouldEqual sample + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftListOperations.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftListOperations.scala new file mode 100644 index 000000000..74fae41d3 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftListOperations.scala @@ -0,0 +1,344 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact.suites + +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers._ +import com.outworkers.util.testing.twitter._ +import org.scalatest.FlatSpec + +class ThriftListOperations extends CompactSuite { + + it should "prepend an item to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList prepend sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample2 :: sample.thriftList) + } + } + + it should "prepend an item to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList prepend sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample2 :: sample.thriftList) + } + } + + it should "prepend several items to a thrift list column" in { + val sample = gen[ThriftRecord] + + val appendable = genList[ThriftTest]() + + val prependedValues = if (cassandraVersion.value < Version.`2.0.13`) appendable.reverse else appendable + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList prepend appendable) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual prependedValues ::: sample.thriftList + } + } + + it should "prepend several items to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val appendable = genList[ThriftTest]() + + val prependedValues = if (cassandraVersion.value < Version.`2.0.13`) appendable.reverse else appendable + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList prepend appendable) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual prependedValues ::: sample.thriftList + } + } + + it should "append an item to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual sample.thriftList :+ sample2 + } + } + + it should "append an item to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual sample.thriftList :+ sample2 + } + } + + it should "append several items to a thrift list column" in { + val sample = gen[ThriftRecord] + val sample2 = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList ::: sample2) + } + } + + it should "append several items to a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val sample2 = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList append sample2) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList ::: sample2) + } + } + + it should "remove an item from a thrift list column" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + _ <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList discard sample2) + .future() + select <- thriftDb.thriftColumnTable + .select(_.thriftList) + .where(_.id eqs sample.id) + .one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff List(sample2)) + } + } + + it should "remove an item from a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + _ <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable + .update.where(_.id eqs sample.id) + .modify(_.thriftList discard sample2) + .future() + select <- thriftDb.thriftColumnTable + .select(_.thriftList) + .where(_.id eqs sample.id) + .one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff List(sample2)) + } + } + + it should "remove several items from a thrift list column" in { + val sample = gen[ThriftRecord] + + val removables = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id) + .modify(_.thriftList discard removables) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff removables) + } + } + + it should "remove several items from a thrift list column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val removables = genList[ThriftTest]() + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id) + .modify(_.thriftList discard removables) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftList diff removables) + } + } + + it should "set an index to a given value" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList setIdx(0, sample2)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.isDefinedAt(2) shouldEqual true + items.value should contain (sample2) + } + } + + it should "set an index to a given value with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftList setIdx(0, sample2)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } + + it should "set a non-zero index to a given value" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftList setIdx(2, sample2)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } + + it should "set a non-zero index to a given value with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val sample2 = gen[ThriftTest] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftList setIdx(2, sample2)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftList).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation.asScala) { items => + items shouldBe defined + items.value should contain (sample2) + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftMapColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftMapColumnTest.scala similarity index 93% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftMapColumnTest.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftMapColumnTest.scala index 4bc6c8740..3efb4f22a 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftMapColumnTest.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftMapColumnTest.scala @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.compact.suites -import com.outworkers.phantom.tables.ThriftRecord import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.compact.CompactSuite import com.outworkers.util.samplers._ -import org.scalatest.FlatSpec -class ThriftMapColumnTest extends FlatSpec with ThriftTestSuite with TwitterFutures { +class ThriftMapColumnTest extends CompactSuite { it should "put an item to a thrift map column" in { val sample = gen[ThriftRecord] diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftSetOperationsTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftSetOperationsTest.scala similarity index 93% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftSetOperationsTest.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftSetOperationsTest.scala index 2a6df07e0..51232ec3a 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftSetOperationsTest.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/suites/ThriftSetOperationsTest.scala @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.compact.suites import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.compact.CompactSuite +import com.outworkers.phantom.thrift.util.ThriftTestSuite import com.outworkers.util.samplers._ import org.scalatest.FlatSpec -import org.scalatest.time.SpanSugar._ -class ThriftSetOperationsTest extends FlatSpec with ThriftTestSuite { +class ThriftSetOperationsTest extends CompactSuite { it should "add an item to a thrift set column" in { @@ -113,10 +114,6 @@ class ThriftSetOperationsTest extends FlatSpec with ThriftTestSuite { .value(_.ref, sample) .value(_.thriftSet, Set(sample, sample2, sample3)) - val q = thriftDb - .thriftColumnTable.update.where(_.id eqs id) - .modify(_.thriftSet removeAll Set(sample2, sample3)) - val operation = for { insertDone <- insert.future() update <- thriftDb diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/tables.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/tables.scala new file mode 100644 index 000000000..84f061d46 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/compact/tables.scala @@ -0,0 +1,63 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.compact + +import com.outworkers.phantom.connectors.CassandraConnection +import com.outworkers.phantom.database.Database +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.tables.Connector +import com.outworkers.phantom.thrift.models._ +import com.outworkers.phantom.thrift.compact._ +import com.outworkers.phantom.thrift.tests.ThriftRecord + +abstract class ThriftColumnTable extends Table[ThriftColumnTable, ThriftRecord] { + object id extends UUIDColumn with PartitionKey + object name extends StringColumn + object ref extends Col[ThriftTest] + + object thriftSet extends SetColumn[ThriftTest] + + object thriftList extends ListColumn[ThriftTest] + + object thriftMap extends MapColumn[String, ThriftTest] + + object optionalThrift extends OptionalCol[ThriftTest] +} + +abstract class ThriftIndexedTable extends Table[ThriftIndexedTable, ThriftRecord] { + + object id extends UUIDColumn + object name extends StringColumn + + object ref extends Col[ThriftTest] with PartitionKey + + object thriftSet extends SetColumn[ThriftTest] + + object thriftList extends ListColumn[ThriftTest] + + object thriftMap extends MapColumn[String, ThriftTest] + + object optionalThrift extends OptionalCol[ThriftTest] +} + +abstract class ThriftDatabase( + override val connector: CassandraConnection +) extends Database[ThriftDatabase](connector) { + object thriftColumnTable extends ThriftColumnTable with Connector + object thriftIndexedTable extends ThriftIndexedTable with Connector +} + +object ThriftDatabase extends ThriftDatabase(Connector.default) \ No newline at end of file diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/TJsonSuite.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/TJsonSuite.scala new file mode 100644 index 000000000..e8a3c6d00 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/TJsonSuite.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.tjson +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import org.scalatest.FlatSpec + +trait TJsonSuite extends FlatSpec with ThriftTestSuite { + val thriftDb = tjson.ThriftDatabase + + override def beforeAll(): Unit = { + super.beforeAll() + thriftDb.create() + } + +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/OptionalThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/OptionalThriftColumnTest.scala new file mode 100644 index 000000000..705f248f4 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/OptionalThriftColumnTest.scala @@ -0,0 +1,73 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.datastax.driver.core.utils.UUIDs +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite +import com.outworkers.util.samplers._ + +class OptionalThriftColumnTest extends TJsonSuite { + + it should "find an item if it was defined" in { + + val id = UUIDs.timeBased() + + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .value(_.thriftList, List(sample)) + .value(_.optionalThrift, Some(sample)) + .future() + + val operation = for { + insertDone <- insert + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + } yield select + + whenReady(operation) { res => + res.value shouldBe Some(sample) + } + } + + it should "not find an item if was not defined" in { + val id = UUIDs.timeBased() + + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .value(_.thriftList, List(sample)) + .value(_.optionalThrift, None) + .future() + + val operation = for { + insertDone <- insert + select <- thriftDb.thriftColumnTable.select(_.optionalThrift).where(_.id eqs id).one + } yield select + + whenReady(operation) { res => + res.value.isDefined shouldBe false + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftColumnTest.scala new file mode 100644 index 000000000..68291e1f6 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftColumnTest.scala @@ -0,0 +1,62 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.datastax.driver.core.utils.UUIDs +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite +import com.outworkers.util.samplers._ + +class ThriftColumnTest extends TJsonSuite { + + it should "allow storing thrift columns" in { + val id = UUIDs.timeBased() + val sample = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .future() flatMap { + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() + } + + whenReady(insert) { result => + result.value.struct shouldEqual sample + } + } + + it should "allow storing lists of thrift objects" in { + val id = UUIDs.timeBased() + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sampleList = Set(sample, sample2) + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, sampleList) + .future() flatMap { + _ => thriftDb.thriftColumnTable.select.where(_.id eqs id).one() + } + + whenReady(insert) { result => + result.value.struct shouldEqual sample + result.value.thriftSet shouldEqual sampleList + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftIndexTableTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftIndexTableTest.scala new file mode 100644 index 000000000..45ce4a436 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftIndexTableTest.scala @@ -0,0 +1,67 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite +import com.outworkers.phantom.thrift.tjson._ +import com.outworkers.util.samplers._ + +class ThriftIndexTableTest extends TJsonSuite { + + it should "allow storing a thrift class inside a table indexed by a thrift struct" in { + val sample = gen[ThriftRecord] + + val chain = for { + _ <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() + } yield get + + whenReady(chain) { res => + res.value.id shouldEqual sample.id + res.value.name shouldEqual sample.name + res.value.struct shouldEqual sample.struct + res.value.optThrift shouldEqual sample.optThrift + res.value.thriftList shouldEqual sample.thriftList + res.value.thriftMap shouldEqual sample.thriftMap + res.value.thriftSet shouldEqual sample.thriftSet + + res.value shouldEqual sample + } + } + + it should "allow storing a thrift class inside a table indexed by a thrift struct with Twitter futures" in { + val sample = gen[ThriftRecord] + + val chain = for { + store <- thriftDb.thriftIndexedTable.store(sample).future() + get <- thriftDb.thriftIndexedTable.select.where(_.ref eqs sample.struct).one() + } yield get + + whenReady(chain) { res => + res.value.id shouldEqual sample.id + res.value.name shouldEqual sample.name + res.value.struct shouldEqual sample.struct + res.value.optThrift shouldEqual sample.optThrift + res.value.thriftList shouldEqual sample.thriftList + res.value.thriftMap shouldEqual sample.thriftMap + res.value.thriftSet shouldEqual sample.thriftSet + + res.value shouldEqual sample + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftListOperations.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftListOperations.scala similarity index 98% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftListOperations.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftListOperations.scala index a189d4e0f..3f29ab429 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftListOperations.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftListOperations.scala @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.tests.tjson.suites import com.outworkers.phantom.finagle._ -import com.outworkers.phantom.tables.{ThriftDatabase, ThriftRecord} +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite import com.outworkers.util.samplers._ import com.outworkers.util.testing.twitter._ -import org.scalatest.FlatSpec -class ThriftListOperations extends FlatSpec with ThriftTestSuite with TwitterFutures { +class ThriftListOperations extends TJsonSuite { it should "prepend an item to a thrift list column" in { val sample = gen[ThriftRecord] diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftMapColumnTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftMapColumnTest.scala new file mode 100644 index 000000000..21cb54bf0 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftMapColumnTest.scala @@ -0,0 +1,106 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers._ +import org.scalatest.FlatSpec + +class ThriftMapColumnTest extends TJsonSuite { + + it should "put an item to a thrift map column" in { + val sample = gen[ThriftRecord] + val toAdd = gen[(String, ThriftTest)] + val expected = sample.thriftMap + toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftMap put toAdd) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldEqual expected + } + } + + it should "put an item to a thrift map column with Twitter Futures" in { + val sample = gen[ThriftRecord] + val toAdd = gen[(String, ThriftTest)] + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftMap put toAdd).future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one() + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldEqual (sample.thriftMap + toAdd) + } + } + + + it should "put several items to a thrift map column" in { + val sample = gen[ThriftRecord] + + val toAdd = genMap[String, ThriftTest]() + val expected = sample.thriftMap ++ toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future + update <- thriftDb.thriftColumnTable.update + .where(_.id eqs sample.id) + .modify(_.thriftMap putAll toAdd).future() + select <- thriftDb.thriftColumnTable + .select(_.thriftMap) + .where(_.id eqs sample.id) + .one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.size shouldEqual expected.size + items.value shouldEqual expected + } + } + + it should "put several items to a thrift map column with Twitter Futures" in { + val sample = gen[ThriftRecord] + + val toAdd = genMap[String, ThriftTest]() + val expected = sample.thriftMap ++ toAdd + + val operation = for { + insertDone <- thriftDb.thriftColumnTable.store(sample).future() + update <- thriftDb.thriftColumnTable.update.where(_.id eqs sample.id).modify(_.thriftMap putAll toAdd).future() + select <- thriftDb.thriftColumnTable.select(_.thriftMap).where(_.id eqs sample.id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value.size shouldEqual expected.size + items.value shouldEqual expected + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftSetOperationsTest.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftSetOperationsTest.scala new file mode 100644 index 000000000..465f1c21d --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftSetOperationsTest.scala @@ -0,0 +1,131 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.thrift.tests.tjson.TJsonSuite +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers._ +import org.scalatest.FlatSpec + +class ThriftSetOperationsTest extends TJsonSuite { + + it should "add an item to a thrift set column" in { + + val id = gen[UUID] + + val sample = gen[ThriftTest] + + val sample2 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet add sample2).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value shouldBe Set(sample, sample2) + } + } + + it should "add several items a thrift set column" in { + + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet addAll Set(sample2, sample3)).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample, sample2, sample3) + } + } + + it should "remove one item from a thrift set column" in { + + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample, sample2, sample3)) + .future() + + val operation = for { + insertDone <- insert + update <- thriftDb.thriftColumnTable.update.where(_.id eqs id).modify(_.thriftSet remove sample3).future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample, sample2) + } + } + + it should "remove several items from thrift set column" in { + val id = gen[UUID] + val sample = gen[ThriftTest] + val sample2 = gen[ThriftTest] + val sample3 = gen[ThriftTest] + + val insert = thriftDb.thriftColumnTable.insert + .value(_.id, id) + .value(_.name, sample.name) + .value(_.ref, sample) + .value(_.thriftSet, Set(sample, sample2, sample3)) + + val operation = for { + insertDone <- insert.future() + update <- thriftDb + .thriftColumnTable.update.where(_.id eqs id) + .modify(_.thriftSet removeAll Set(sample2, sample3)) + .future() + select <- thriftDb.thriftColumnTable.select(_.thriftSet).where(_.id eqs id).one + } yield select + + whenReady(operation) { items => + items shouldBe defined + items.value should contain theSameElementsAs Set(sample) + } + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftTJsonSerializationTests.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftTJsonSerializationTests.scala new file mode 100644 index 000000000..18b2cebe7 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/suites/ThriftTJsonSerializationTests.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson.suites + +import com.datastax.driver.core.ProtocolVersion +import com.outworkers.phantom.builder.primitives.Primitive +import com.outworkers.phantom.thrift.tjson._ +import com.outworkers.phantom.thrift.util.ThriftTestSuite +import com.outworkers.util.samplers.Sample +import org.scalacheck.Gen +import org.scalatest.prop.GeneratorDrivenPropertyChecks +import org.scalatest.{Assertion, FlatSpec} + +class ThriftTJsonSerializationTests extends FlatSpec with ThriftTestSuite with GeneratorDrivenPropertyChecks { + + val protocolGen: Gen[ProtocolVersion] = Gen.alphaChar.map(_ => ProtocolVersion.V5) + + def roundtrip[T : Primitive](gen: Gen[T]): Assertion = { + val ev = Primitive[T] + forAll(gen, protocolGen) { (sample, protocol) => + ev.deserialize(ev.serialize(sample, protocol), protocol) shouldEqual sample + } + } + + def sroundtrip[T : Primitive : Sample]: Assertion = { + roundtrip[T](Sample.arbitrary[T].arbitrary) + } + + it should "serialize binary thrift primitives" in { + sroundtrip[ThriftTest] + } + + it should "serialize lists of binary thrift primitives" in { + sroundtrip[List[ThriftTest]] + } + + it should "serialize sets of binary thrift primitives" in { + sroundtrip[Set[ThriftTest]] + } + + it should "serialize maps of binary thrift primitives with thrift values" in { + sroundtrip[Map[String, ThriftTest]] + } + + + it should "serialize maps of binary thrift primitives with thrift keys" in { + sroundtrip[Map[ThriftTest, String]] + } + + it should "serialize maps of binary thrift primitives with thrift keys and values" in { + sroundtrip[Map[ThriftTest, ThriftTest]] + } +} diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/tables.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/tables.scala new file mode 100644 index 000000000..f7797cd52 --- /dev/null +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/tests/tjson/tables.scala @@ -0,0 +1,63 @@ +/* + * Copyright 2013 - 2017 Outworkers Ltd. + * + * 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.outworkers.phantom.thrift.tests.tjson + +import com.outworkers.phantom.connectors.CassandraConnection +import com.outworkers.phantom.database.Database +import com.outworkers.phantom.dsl._ +import com.outworkers.phantom.tables.Connector +import com.outworkers.phantom.thrift.models._ +import com.outworkers.phantom.thrift.tests.ThriftRecord +import com.outworkers.phantom.thrift.tjson._ + +abstract class ThriftColumnTable extends Table[ThriftColumnTable, ThriftRecord] { + object id extends UUIDColumn with PartitionKey + object name extends StringColumn + object ref extends Col[ThriftTest] + + object thriftSet extends SetColumn[ThriftTest] + + object thriftList extends ListColumn[ThriftTest] + + object thriftMap extends MapColumn[String, ThriftTest] + + object optionalThrift extends OptionalCol[ThriftTest] +} + +abstract class ThriftIndexedTable extends Table[ThriftIndexedTable, ThriftRecord] { + + object id extends UUIDColumn + object name extends StringColumn + + object ref extends Col[ThriftTest] with PartitionKey + + object thriftSet extends SetColumn[ThriftTest] + + object thriftList extends ListColumn[ThriftTest] + + object thriftMap extends MapColumn[String, ThriftTest] + + object optionalThrift extends OptionalCol[ThriftTest] +} + +abstract class ThriftDatabase( + override val connector: CassandraConnection +) extends Database[ThriftDatabase](connector) { + object thriftColumnTable extends ThriftColumnTable with Connector + object thriftIndexedTable extends ThriftIndexedTable with Connector +} + +object ThriftDatabase extends ThriftDatabase(Connector.default) \ No newline at end of file diff --git a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftTestSuite.scala b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/util/ThriftTestSuite.scala similarity index 71% rename from phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftTestSuite.scala rename to phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/util/ThriftTestSuite.scala index 3093df765..197c7e4a0 100644 --- a/phantom-thrift/src/test/scala/com/outworkers/phantom/suites/ThriftTestSuite.scala +++ b/phantom-thrift/src/test/scala/com/outworkers/phantom/thrift/util/ThriftTestSuite.scala @@ -13,27 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.outworkers.phantom.suites +package com.outworkers.phantom.thrift.util import java.util.UUID import com.outworkers.phantom.PhantomSuite -import com.outworkers.phantom.tables.{ThriftDatabase, ThriftRecord} -import com.outworkers.phantom.finagle._ +import com.outworkers.phantom.thrift.tests.ThriftRecord import com.outworkers.util.samplers._ +import com.twitter.util.{Future, Return, Throw} -trait ThriftTestSuite extends PhantomSuite with TwitterFutures { +trait ThriftTestSuite extends PhantomSuite { - def thriftDb: ThriftDatabase = ThriftDatabase + implicit def twitterFutureToConcept[T](f: Future[T]): FutureConcept[T] = new FutureConcept[T] { + override def eitherValue: Option[Either[Throwable, T]] = f.poll match { + case Some(Return(ret)) => Some(Right(ret)) + case Some(Throw(err)) => Some(Left(err)) + case None => None + } + + override def isExpired: Boolean = false - override def beforeAll(): Unit = { - super.beforeAll() - thriftDb.create() + override def isCanceled: Boolean = false } type ThriftTest = com.outworkers.phantom.thrift.models.ThriftTest val ThriftTest = com.outworkers.phantom.thrift.models.ThriftTest + implicit object ThriftTestSample extends Sample[ThriftTest] { + def sample: ThriftTest = ThriftTest( + gen[Int], + gen[String], + test = false + ) + } + implicit object OutputSample extends Sample[ThriftRecord] { def sample: ThriftRecord = { ThriftRecord( @@ -50,11 +63,4 @@ trait ThriftTestSuite extends PhantomSuite with TwitterFutures { } } - implicit object ThriftTestSample extends Sample[ThriftTest] { - def sample: ThriftTest = ThriftTest( - gen[Int], - gen[String], - test = false - ) - } }