Skip to content
This repository was archived by the owner on May 14, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 0 additions & 53 deletions fury

This file was deleted.

3 changes: 2 additions & 1 deletion src/core/QueryBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import language.experimental.macros
import scala.collection.immutable.SortedMap
import com.google.cloud.datastore.StructuredQuery.OrderBy.Direction
import com.google.cloud.datastore.StructuredQuery.OrderBy
import com.google.cloud.datastore.{Query => DatastoreQuery}
import quarantine._

case class QueryBuilder[T] private[mutatus] (
Expand Down Expand Up @@ -62,7 +63,7 @@ case class QueryBuilder[T] private[mutatus] (
decoder: Decoder[T]
): mutatus.Result[Stream[mutatus.Result[T]]] = {
val baseQuery = namespace.option.foldLeft(
Query.newEntityQueryBuilder().setKind(kind)
DatastoreQuery.newEntityQueryBuilder().setKind(kind)
)(_.setNamespace(_))
val filtered = filterCriteria.foldLeft(baseQuery)(_.setFilter(_))
val ordered = orderCriteria.headOption.foldLeft(filtered)(
Expand Down
23 changes: 15 additions & 8 deletions src/core/QueryBuilderMacros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,26 @@ class QueryBuilderMacros(val c: blackbox.Context) {
q"_root_.com.google.cloud.datastore.StructuredQuery.PropertyFilter"
private val composite =
q"_root_.com.google.cloud.datastore.StructuredQuery.CompositeFilter.and"
private val operationMapping
: PartialFunction[c.Tree, (String, Option[c.Tree]) => c.Tree] = {
case q"==" | q"equals" => (path, args) => q"$filter.eq($path, ${args.get})"
case q"<" => (path, args) => q"$filter.lt($path, ${args.get})"
case q"<=" => (path, args) => q"$filter.le($path, ${args.get})"
case q">" => (path, args) => q"$filter.gt($path, ${args.get})"
case q">=" => (path, args) => q"$filter.ge($path, ${args.get})"

def encodedArg(arg: Option[c.Tree]): c.Tree = {
val tpe = c.typecheck(arg.get).tpe
val tpeErasure = tpe.erasure
val usedTpe = if(tpeErasure != tpe && tpe.typeSymbol == tpeErasure.typeSymbol) tpe.erasure else tpe //Used to handle erasure of Int(1) => Int and also keeping in mind to not erase value classes
q"implicitly[_root_.mutatus.Encoder[$usedTpe]].encode(${arg.get})"
}

private val operationMapping: PartialFunction[c.Tree, (String, Option[c.Tree]) => c.Tree] = {
case q"==" | q"equals" => (path, args) => q"$filter.eq($path, ${encodedArg(args)})"
case q"<" => (path, args) => q"$filter.lt($path, ${encodedArg(args)})"
case q"<=" => (path, args) => q"$filter.le($path, ${encodedArg(args)})"
case q">" => (path, args) => q"$filter.gt($path, ${encodedArg(args)})"
case q">=" => (path, args) => q"$filter.ge($path, ${encodedArg(args)})"
case q"isEmpty" => (path, args) => q"$filter.isNull($path)"
case q"isDefined" | q"nonEmpty" =>
(path, args) =>
q"$filter.gt($path, _root_.com.google.cloud.datastore.NullValue.of())"
case q"contains" | q"foreach" =>
(path, args) => q"$filter.eq($path, ${args.get})"
(path, args) => q"$filter.eq($path, ${encodedArg(args)})"
}

def filterImpl[T: c.WeakTypeTag](
Expand Down
34 changes: 26 additions & 8 deletions src/core/mutatus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import scala.annotation.StaticAnnotation
import scala.collection.JavaConverters._
import scala.collection.generic.CanBuildFrom
import scala.language.experimental.macros
import io.opencensus.trace.Status.CanonicalCode
import com.google.rpc.Code

/** Mutatus package object */
Expand Down Expand Up @@ -149,7 +148,7 @@ object `package` extends Domain[MutatusException] {
}

final class id() extends StaticAnnotation

final class notIndexed() extends StaticAnnotation
/** a reference to another case class instance stored in the GCP Datastore */
case class Ref[T](ref: Key) {

Expand Down Expand Up @@ -331,7 +330,6 @@ object Decoder extends Decoder_1 {
def combine[T](caseClass: CaseClass[Decoder, T]): Decoder[T] = {
case entityValue: EntityValue =>
val entity = entityValue.get()

caseClass.constructMonadic { param =>
if (entity.contains(param.label)) {
param.typeclass.decodeValue(entity.getValue(param.label))
Expand All @@ -347,7 +345,9 @@ object Decoder extends Decoder_1 {
}
}
}
}
case value if caseClass.isValueClass=>
caseClass.constructMonadic{ param => param.typeclass.decodeValue(value) }
}

/** tries to decode entity based on encoded `_meta.typename` value containing name of class,
* if such value is missing tries `Decoder`s for each subtype of sealed trait `T` until one doesn't throw an exception
Expand Down Expand Up @@ -479,21 +479,39 @@ object Encoder extends Encoder_1 {
final val metaField = "_meta"
final val typenameField = "typename"

private def excludeFromIndex(value: Value[_]): Value[_] = {
value match {
case coll: ListValue => ListValue.of {
coll.toBuilder.get().asScala.map(excludeFromIndex).asJava
}
case _ => value.toBuilder
.setExcludeFromIndexes(true).asInstanceOf[ValueBuilder[_,_,_]]
.build.asInstanceOf[Value[Any]]
}
}

/** combines `Encoder`s for each parameter of the case class `T` into a `Encoder` for `T` */
def combine[T](caseClass: CaseClass[Encoder, T]): Encoder[T] =
value =>
EntityValue.of {
if(caseClass.isValueClass) {
val param = caseClass.parameters.head
param.typeclass.encode(param.dereference(value)).asInstanceOf[Value[Any]]
} else EntityValue.of {
caseClass.parameters
.to[List]
.toList
.foldLeft(FullEntity.newBuilder()) {
case (b, param) =>
val encodedValue = param.typeclass.encode(param.dereference(value))
val isNotIndexed = param.annotations.find(_.isInstanceOf[notIndexed])
b.set(
param.label,
param.typeclass.encode(param.dereference(value))
isNotIndexed.foldLeft[Value[_]](encodedValue) {
case (value, _) => excludeFromIndex(value)
}
)
}
.build()
}
}

/** chooses the appropriate `Encoder` of a subtype of the sealed trait `T` based on its type */
def dispatch[T](sealedTrait: SealedTrait[Encoder, T]): Encoder[T] =
Expand Down
13 changes: 12 additions & 1 deletion src/tests/QueryBuilderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -237,17 +237,28 @@ case class QueryBuilderSpec()(implicit runner: Runner) {
.reverse
.sortBy(_.intParam)
}.assert { query =>
query.orderCriteria.foreach(println)
query.orderCriteria.toList == List(
OrderBy.asc("intParam"),
OrderBy.desc("innerClass.deeperOpt.optInner.some3"),
OrderBy.asc("innerClassOpt.deeper.some2")
)
}

test("build filter with value classes") {
val id = EntityId(2)
Dao[Entity].all
.filter(_.id == id)
}.assert { _.filterCriteria.contains{
PropertyFilter.eq("id", LongValue.of(2))
}
}
}

object QueryBuilderSpec {
object Model {
case class EntityId(id: Long) extends AnyVal
case class Entity(id: EntityId, value: String)

case class InnerClass3(some3: Int = 3, none: Option[String] = None)
case class InnerClass2(
some2: Int = 2,
Expand Down