Skip to content

Commit

Permalink
dsl
Browse files Browse the repository at this point in the history
  • Loading branch information
ValdemarGr committed Sep 2, 2023
1 parent f654014 commit 6a3a3d7
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 108 deletions.
140 changes: 68 additions & 72 deletions modules/relational/src/main/scala/gql/relational/ExampleImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,12 @@ object SkunkSchema extends QueryAlgebra with QueryDsl {
type Decoder[A] = skunk.Decoder[A]
def optDecoder[A](d: Decoder[A]): Decoder[Option[A]] = d.opt

case class SkunkRunQuery[F[_]: MonadCancelThrow](pool: Resource[F, Session[F]]) extends RunQuery[F] {
def apply[A](query: AppliedFragment, decoder: Decoder[A]): F[List[A]] =
pool.use(_.execute(query.fragment.query(decoder))(query.argument))
type Connection[F[_]] = Resource[F, Session[F]]
implicit def skunkQueryable[F[_]: MonadCancelThrow]: Queryable[F] = new Queryable[F] {
def apply[A](query: AppliedFragment, decoder: Decoder[A], connection: Connection[F]): F[List[A]] =
connection.use(_.execute(query.fragment.query(decoder))(query.argument))
}

def runField[F[_]: MonadCancelThrow, G[_], I, B, ArgType](pool: Resource[F, Session[F]], arg: Arg[ArgType])(
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)]
)(implicit tpe: => Out[F, G[QueryResult[B]]]) =
Field(resolveQuery(EmptyableArg.Lift(arg), q, SkunkRunQuery(pool)), Eval.later(tpe))

def runField[F[_]: MonadCancelThrow, G[_], I, B](pool: Resource[F, Session[F]])(
q: NonEmptyList[I] => Query[G, (Query.Select[I], B)]
)(implicit tpe: => Out[F, G[QueryResult[B]]]) =
Field(resolveQuery[F, G, I, B, Unit](EmptyableArg.Empty, (i, _) => q(i), SkunkRunQuery(pool)), Eval.later(tpe))

trait SkunkTable[A] extends Table[A] {
def aliased(x: Fragment[Void]): Fragment[Void] =
sql"#${alias}.${x}"
Expand All @@ -56,14 +47,75 @@ object SkunkSchema extends QueryAlgebra with QueryDsl {
}
}

type Connection[F[_]] = LazyResource[F, Session[F]]
def lazyPool[F[_]: Concurrent](pool: Resource[F, Session[F]]): Resource[F, Connection[F]] =
type LazyConnection[F[_]] = LazyResource[F, Session[F]]
def lazyPool[F[_]: Concurrent](pool: Resource[F, Session[F]]): Resource[F, LazyConnection[F]] =
LazyResource.fromResource(pool)
}

class MySchema(pool: Resource[IO, Session[IO]]) {
import SkunkSchema._
import MySchema._


implicit lazy val pet: Type[IO, QueryResult[PetTable]] = tpe[IO, QueryResult[PetTable]](
"Pet",
"name" -> query(_.selName),
"id" -> query(_.selId)
)

implicit lazy val entity2: Type[IO, QueryResult[EntityTable2]] = tpe[IO, QueryResult[EntityTable2]](
"Entity",
"name" -> query(_.selName),
"id" -> query(_.selId),
"age" -> query(_.selAge),
"height" -> query(_.selHeight),
"pets" -> cont { e =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} = ${e.id}".apply(Void))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield p
}
/*"pets" -> queryAndThen[IO, Lambda[X => X], EntityTable2, UUID, List[QueryResult[PetTable]]](_.selId)(
_.andThen(
resolveQuery(
EmptyableArg.Empty,
{ (is: NonEmptyList[UUID], _: Unit) =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} in ${uuid.list(is.size).values}".apply(is.toList))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield (pe.selEntityId, p)
},
SkunkRunQuery(pool)
)
)
)*/
)

implicit lazy val entity: Type[IO, QueryResult[EntityTable]] = tpe[IO, QueryResult[EntityTable]](
"Entity",
"name" -> query(_.selName),
"id" -> query(_.selId),
"age" -> query(_.selAge),
"height" -> query(_.selHeight)
)

implicit lazy val contract: Type[IO, QueryResult[ContractTable]] = tpe[IO, QueryResult[ContractTable]](
"Contract",
"name" -> query(c => (c.selName, c.selId).mapN(_ + _.toString())),
"id" -> query(_.selId),
"fastEntities" -> cont(arg[Option[List[String]]]("entityNames")) { (c, ens) =>
entityTable2.join[List] { e =>
val _ = ens.foldMap(xs => sql" and ${e.name} in (${text.list(xs)})".apply(xs))
val extra = void""
sql"${c.id} = ${e.contractId}${extra.fragment}".apply(extra.argument)
}
}
)

}

object MySchema {
import SkunkSchema._
case class EntityTable(alias: String) extends SkunkTable[UUID] {
def table = void"entity"
def groupingKey = void"id"
Expand Down Expand Up @@ -128,60 +180,4 @@ class MySchema(pool: Resource[IO, Session[IO]]) {
val (entityId, selEntityId) = sel("entity_id", uuid)
}
val petEntityTable = table(PetEntityTable)

implicit lazy val pet: Type[IO, QueryResult[PetTable]] = tpe[IO, QueryResult[PetTable]](
"Pet",
"name" -> query(_.selName),
"id" -> query(_.selId)
)

implicit lazy val entity2: Type[IO, QueryResult[EntityTable2]] = tpe[IO, QueryResult[EntityTable2]](
"Entity",
"name" -> query(_.selName),
"id" -> query(_.selId),
"age" -> query(_.selAge),
"height" -> query(_.selHeight),
"pets" -> cont { e =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} = ${e.id}".apply(Void))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield p
}
/*"pets" -> queryAndThen[IO, Lambda[X => X], EntityTable2, UUID, List[QueryResult[PetTable]]](_.selId)(
_.andThen(
resolveQuery(
EmptyableArg.Empty,
{ (is: NonEmptyList[UUID], _: Unit) =>
for {
pe <- petEntityTable.join[List](pe => sql"${pe.entityId} in ${uuid.list(is.size).values}".apply(is.toList))
p <- petTable.join(p => sql"${p.id} = ${pe.petId}".apply(Void))
} yield (pe.selEntityId, p)
},
SkunkRunQuery(pool)
)
)
)*/
)

implicit lazy val entity: Type[IO, QueryResult[EntityTable]] = tpe[IO, QueryResult[EntityTable]](
"Entity",
"name" -> query(_.selName),
"id" -> query(_.selId),
"age" -> query(_.selAge),
"height" -> query(_.selHeight)
)

implicit lazy val contract: Type[IO, QueryResult[ContractTable]] = tpe[IO, QueryResult[ContractTable]](
"Contract",
"name" -> query(c => (c.selName, c.selId).mapN(_ + _.toString())),
"id" -> query(_.selId),
"fastEntities" -> cont(arg[Option[List[String]]]("entityNames")) { (c, ens) =>
entityTable2.join[List] { e =>
val _ = ens.foldMap(xs => sql" and ${e.name} in (${text.list(xs)})".apply(xs))
val extra = void""
sql"${c.id} = ${e.contractId}${extra.fragment}".apply(extra.argument)
}
}
)

}
}
25 changes: 14 additions & 11 deletions modules/relational/src/main/scala/gql/relational/QueryAlgebra.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import cats.arrow.FunctionK
import gql.ast._
import gql.EmptyableArg
import gql.resolver.FieldMeta
import gql._

trait QueryAlgebra {
import QueryAlgebra.{QueryState => _, QueryStateImpl => _, _}
Expand All @@ -24,29 +25,31 @@ trait QueryAlgebra {
def stringToFrag(s: String): Frag
implicit def appliedFragmentMonoid: Monoid[Frag]

trait RunQuery[F[_]] {
def apply[A](query: Frag, decoder: Decoder[A]): F[List[A]]
type Connection[F[_]]
trait Queryable[F[_]] {
def apply[A](query: Frag, decoder: Decoder[A], connection: Connection[F]): F[List[A]]
}
def resolveQuery[F[_], G[_], I, B, ArgType](

def resolveQuery[F[_]: Queryable, G[_], I, B, ArgType](
toplevelArg: EmptyableArg[ArgType],
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)],
runQuery: RunQuery[F]
connection: Connection[F]
)(implicit F: Applicative[F]): Resolver[F, I, G[QueryResult[B]]] =
compileToResolver[F, G, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
evalQuery(xs, fm, q(xs, at), runQuery)
evalQuery(xs, fm, q(xs, at), connection)
}.emap(_.toIor)

def resolveQuerySingle[F[_], G[_], I, B, ArgType](
def resolveQuerySingle[F[_]: Queryable, G[_], I, B, ArgType](
toplevelArg: EmptyableArg[ArgType],
q: (I, ArgType) => Query[G, B],
runQuery: RunQuery[F]
connection: Connection[F]
)(implicit F: Applicative[F]): Resolver[F, I, G[QueryResult[B]]] =
compileToResolver[F, G, I, ArgType, Either[String, G[QueryResult[B]]]](toplevelArg) { (xs, at, fm) =>
xs.toList
.traverse { x =>
val baseQuery = q(x, at)
val moddedQuery = baseQuery.map(b => (Applicative[Query.Select].pure(x), b))
evalQuery(NonEmptyList.one(x), fm, moddedQuery, runQuery)
evalQuery(NonEmptyList.one(x), fm, moddedQuery, connection)
}
.map(_.flatMap(_.toList).toMap)
}.emap(_.toIor)
Expand All @@ -55,8 +58,8 @@ trait QueryAlgebra {
xs: NonEmptyList[I],
fm: FieldMeta[F],
query: Query[G, (Query.Select[I], B)],
runQuery: RunQuery[F]
)(implicit F: Applicative[F]): F[Map[I, Either[String, G[QueryResult[B]]]]] = {
connection: Connection[F]
)(implicit F: Applicative[F], queryable: Queryable[F]): F[Map[I, Either[String, G[QueryResult[B]]]]] = {
val eff = Interpreter.collapseQuery(query).flatMap { qs =>
val out = Interpreter.compileQueryState(fm.astNode, qs.map { case (_, b) => b }, FieldVariant.SubSelection[B]())
out tupleLeft qs.map { case (sel, _) => sel }.value
Expand All @@ -71,7 +74,7 @@ trait QueryAlgebra {
val qc2 = Interpreter.QueryContent(sel.cols ++ qc.selections, qc.joins)
val frag = Interpreter.renderQuery(qc2)
println(s"running:\n${frag.asInstanceOf[skunk.AppliedFragment].fragment.sql}\n")
val result = runQuery(frag, decoder)
val result = queryable(frag, decoder, connection)
result
.map{ xs => println(s"got ${xs.size} results"); xs }
.map(_.groupMap { case (k, _) => k } { case (_, v) => v })
Expand Down
83 changes: 60 additions & 23 deletions modules/relational/src/main/scala/gql/relational/QueryDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import natchez.Kernel
import natchez.Span
import cats.arrow.FunctionK

trait QueryDsl extends QueryAlgebra {
trait QueryDsl extends QueryAlgebra { self =>
def query[F[_], G[_], A, B](f: A => Query[G, Query.Select[B]])(implicit
tpe: => Out[F, G[B]]
): Field[F, QueryResult[A], G[B]] =
Expand All @@ -39,18 +39,6 @@ trait QueryDsl extends QueryAlgebra {
): Field[F, QueryResult[A], G[B]] =
queryFull(EmptyableArg.Lift(a))((a, c) => f(a, c), Resolver.id[F, G[B]])(tpe)

def queryAndThen[F[_], G[_], A, B, D](f: A => Query[G, Query.Select[B]])(g: Resolver[F, G[B], G[B]] => Resolver[F, G[B], D])(implicit
tpe: => Out[F, D]
): Field[F, QueryResult[A], D] =
queryFull(EmptyableArg.Empty)((a, _) => f(a), g(Resolver.id[F, G[B]]))(tpe)

def queryAndThen[F[_], G[_], A, B, C, D](
a: Arg[C]
)(f: (A, C) => Query[G, Query.Select[B]])(g: Resolver[F, G[B], G[B]] => Resolver[F, G[B], D])(implicit
tpe: => Out[F, D]
): Field[F, QueryResult[A], D] =
queryFull(EmptyableArg.Lift(a))((a, c) => f(a, c), g(Resolver.id[F, G[B]]))(tpe)

def cont[F[_], G[_], A, B](f: A => Query[G, B])(implicit
tpe: => Out[F, G[QueryResult[B]]]
): Field[F, QueryResult[A], G[QueryResult[B]]] =
Expand All @@ -61,6 +49,16 @@ trait QueryDsl extends QueryAlgebra {
): Field[F, QueryResult[A], G[QueryResult[B]]] =
contFull(EmptyableArg.Lift(a))((a, c) => f(a, c))(tpe)

def runField[F[_]: Queryable: Applicative, G[_], I, B, ArgType](connection: Connection[F], arg: Arg[ArgType])(
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)]
)(implicit tpe: => Out[F, G[QueryResult[B]]]) =
Field(resolveQuery(EmptyableArg.Lift(arg), q, connection), Eval.later(tpe))

def runField[F[_]: Queryable: Applicative, G[_], I, B](connection: Connection[F])(
q: NonEmptyList[I] => Query[G, (Query.Select[I], B)]
)(implicit tpe: => Out[F, G[QueryResult[B]]]) =
Field(resolveQuery[F, G, I, B, Unit](EmptyableArg.Empty, (i, _) => q(i), connection), Eval.later(tpe))

def table[T <: Table[?]](f: String => T): TableAlg[T] = new TableAlg[T] {
def make: String => T = f
}
Expand Down Expand Up @@ -111,16 +109,55 @@ trait QueryDsl extends QueryAlgebra {
final class RelationalFieldBuilder[F[_], A](private val dummy: Boolean = false) {
def tpe(name: String, hd: (String, Field[F, QueryResult[A], ?]), tl: (String, Field[F, QueryResult[A], ?])*): Type[F, QueryResult[A]] =
gql.dsl.tpe[F, QueryResult[A]](name, hd, tl: _*)
}

final class PartialQuery[G[_], A, B, C, D](
ea: EmptyableArg[B],
g: D => Query[G, Query.Select[C]],
f: (A, B) => D
) {
def apply[F[_], E](h: Resolver[F, G[C], G[C]] => Resolver[F, G[C], E])(implicit
tpe: => Out[F, E]
): Field[F, QueryResult[A], E] =
queryFull[F, G, A, C, B, E](ea)((a, b) => g(f(a, b)), h(Resolver.id[F, G[C]]))(tpe)
def queryAndThen[G[_], B, C](f: A => Query[G, Query.Select[B]])(g: Resolver[F, G[B], G[B]] => Resolver[F, G[B], C])(
tpe: => Out[F, C]
): Field[F, QueryResult[A], C] =
queryFull(EmptyableArg.Empty)((a, _) => f(a), g(Resolver.id[F, G[B]]))(tpe)

def queryAndThen[G[_], B, C, D](
a: Arg[C]
)(f: (A, C) => Query[G, Query.Select[B]])(g: Resolver[F, G[B], G[B]] => Resolver[F, G[B], D])(implicit
tpe: => Out[F, D]
): Field[F, QueryResult[A], D] =
/* queryFull(EmptyableArg.Lift(a))((a, c) => f(a, c), g(Resolver.id[F, G[B]]))(tpe)
def contBoundary[G[_], B, C, D](connection: Connection[F])(f: A => Query[G, Query.Select[B]])(
continue: NonEmptyList[B] => Query[G, C]
)(implicit F: Applicative[F], Q: Queryable[F], tpe: => Out[F, G[QueryResult[B]]]) =
Field(
queryFull(EmptyableArg.Empty)((i, _) => q(a), Resolver.id[F, G[B]])(tpe)
)
Field(queryAndThen(f)(_.andThen(resolveQuery(EmptyableArg.Empty, (i, _) => q(i), connection))), Eval.later(tpe))
*/
def query[G[_], B](f: A => Query[G, Query.Select[B]])(implicit
tpe: => Out[F, G[B]]
): Field[F, QueryResult[A], G[B]] =
self.query(f)(tpe)

def query[G[_], B, C](a: Arg[C])(f: (A, C) => Query[G, Query.Select[B]])(implicit
tpe: => Out[F, G[B]]
): Field[F, QueryResult[A], G[B]] =
self.query(a)(f)(tpe)

def cont[G[_], B](f: A => Query[G, B])(implicit
tpe: => Out[F, G[QueryResult[B]]]
): Field[F, QueryResult[A], G[QueryResult[B]]] =
self.cont(f)(tpe)

def cont[G[_], B, C](a: Arg[C])(f: (A, C) => Query[G, B])(implicit
tpe: => Out[F, G[QueryResult[B]]]
): Field[F, QueryResult[A], G[QueryResult[B]]] =
self.cont(a)(f)(tpe)

def runField[G[_], I, B, ArgType](connection: Connection[F], arg: Arg[ArgType])(
q: (NonEmptyList[I], ArgType) => Query[G, (Query.Select[I], B)]
)(implicit F: Applicative[F], Q: Queryable[F], tpe: => Out[F, G[QueryResult[B]]]) =
self.runField(connection, arg)(q)(Q, F, tpe)

def runField[G[_], I, B](connection: Connection[F])(
q: NonEmptyList[I] => Query[G, (Query.Select[I], B)]
)(implicit F: Applicative[F], Q: Queryable[F], tpe: => Out[F, G[QueryResult[B]]]) =
self.runField(connection)(q)(Q, F, tpe)
}
}
6 changes: 4 additions & 2 deletions modules/relational/src/main/scala/gql/relational/a.scala
Original file line number Diff line number Diff line change
Expand Up @@ -636,13 +636,15 @@ object Test7 {
import gql.relational.MySchema
import gql.relational.SkunkSchema
val ms = new MySchema(xaPool)
import MySchema._
import ms._
implicit val c0 = ms.contract
val ss = SchemaShape.unit[IO](
fields[IO, Unit](
"name" -> lift(_ => "edlav"),
"contract" -> SkunkSchema.runField(xaPool, arg[UUID]("contractId"))((_: NonEmptyList[Unit], a: UUID) =>
ms.contractTable.join[Option](c => sql"${c.id} = ${uuid}".apply(a)).map(t => (().pure[SkunkSchema.Query.Select], t))
)(implicitly, gql.ast.gqlOutForOption(ms.contract))
MySchema.contractTable.join[Option](c => sql"${c.id} = ${uuid}".apply(a)).map(t => (().pure[SkunkSchema.Query.Select], t))
)
)
)

Expand Down

0 comments on commit 6a3a3d7

Please sign in to comment.