All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gql.relational.QueryDsl.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2024 Valdemar Grange
 *
 * 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 gql.relational

import gql.ast._
import gql.dsl.all._
import cats.implicits._
import cats._
import gql.resolver.Resolver
import cats.data._
import gql.Arg
import gql.EmptyableArg
import cats.arrow.FunctionK
import org.typelevel.scalaccompat.annotation._

// For all query algebras this dsl can exist
abstract class QueryDsl[QA <: QueryAlgebra](val algebra: QA) { self =>
  import algebra._

  private type QueryContext[A] = algebra.QueryContext[A]

  def select[A](decoder: algebra.Decoder[A], colHd: Frag, colTl: Frag*): Query.Select[A] =
    Query.Select(Chain(colHd) ++ Chain.fromSeq(colTl), decoder)

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

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

  def cont[F[_], G[_], A, B](f: A => Query[G, B])(implicit
      tpe: => Out[F, G[QueryContext[B]]]
  ): Field[F, QueryContext[A], G[QueryContext[B]]] =
    contFull[F, G, A, B, Unit](EmptyableArg.Empty)((a, _) => f(a))(tpe)

  def cont[F[_], G[_], A, B, C](a: Arg[C])(f: (A, C) => Query[G, B])(implicit
      tpe: => Out[F, G[QueryContext[B]]]
  ): Field[F, QueryContext[A], G[QueryContext[B]]] =
    contFull[F, G, A, B, C](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[QueryContext[B]]]) =
    Field(resolveQuery(EmptyableArg.Lift(arg), q, connection), Eval.always(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[QueryContext[B]]]) =
    Field(resolveQuery[F, G, I, B, Unit](EmptyableArg.Empty, (i, _) => q(i), connection), Eval.always(tpe))

  def runFieldSingle[F[_]: Queryable: Applicative, G[_], I, B, ArgType](connection: Connection[F], arg: Arg[ArgType])(
      q: (I, ArgType) => Query[G, B]
  )(implicit tpe: => Out[F, G[QueryContext[B]]]): Field[F, I, G[QueryContext[B]]] =
    Field(resolveQuerySingle(EmptyableArg.Lift(arg), q, connection), Eval.always(tpe))

  def runFieldSingle[F[_]: Queryable: Applicative, G[_], I, B](connection: Connection[F])(
      q: I => Query[G, B]
  )(implicit tpe: => Out[F, G[QueryContext[B]]]): Field[F, I, G[QueryContext[B]]] =
    Field(resolveQuerySingle[F, G, I, B, Unit](EmptyableArg.Empty, (i, _) => q(i), connection), Eval.always(tpe))

  final class BuildWithBuilder[F[_], A] {
    def apply[B](f: RelationalFieldBuilder[F, A] => B): B = f(new RelationalFieldBuilder[F, A]())
  }

  def relBuilder[F[_], A] = new BuildWithBuilder[F, A]

  def newAlias: Query[λ[X => X], String] = algebra.Query.liftF(algebra.nextId)

  def joinFull[A](make: String => A, pred: A => Frag, join: A => Frag): algebra.Query[λ[X => X], A] =
    for {
      n <- newAlias
      a = make(n)
      p = pred(a)
      j = join(a)
      _ <- algebra.Query.liftF(algebra.addJoin(j, p))
    } yield a

  // This is potentially unsafe and pretty low-level
  // Take a look at [[reassociate]] for a safer version and ideas of how to use this properly
  def reassociateFull[G[_], Key](reassoc: QueryAlgebra.Reassoc[G, Key], dec: algebra.Decoder[Key], cols: Frag*): Query[G, Unit] =
    algebra.Query.liftEffect[G, Unit](
      algebra.addSelection(Chain.fromSeq(cols)).as {
        QueryAlgebra.QueryState[algebra.Decoder, G, Key, Unit, G](
          reassoc,
          dec,
          (),
          FunctionK.id[G]
        )
      }
    )

  def reassociate[G[_]: QueryAlgebra.JoinType](sel: Query.Select[?]): Query[G, Unit] = {
    def go[B](sel: Query.Select[B]) =
      reassociateFull[G, Option[B]](
        QueryAlgebra.ReassocOpt(implicitly[QueryAlgebra.JoinType[G]].reassoc[B]),
        algebra.optDecoder(sel.decoder),
        sel.cols.toList: _*
      )
    go(sel)
  }

  trait TableAlg[T <: Table] {
    def make: String => T

    def simpleJoin(joinPred: T => Frag): algebra.Query[λ[X => X], T] =
      joinFull[T](make, joinPred, t => t.table |+| stringToFrag(" as ") |+| stringToFrag(t.alias))

    def join[G[_]: QueryAlgebra.JoinType](joinPred: T => Frag): algebra.Query[G, T] =
      for {
        t <- simpleJoin(joinPred)
        _ <- reassociate[G](t.tableKey)
      } yield t
  }

  def table[T <: Table](f: String => T): TableAlg[T] = new TableAlg[T] {
    def make: String => T = f
  }

  def queryFull[F[_], G[_], A, B, C, D](a: EmptyableArg[C])(f: (A, C) => Query[G, Query.Select[B]], resolverCont: Resolver[F, G[B], D])(
      implicit tpe: => Out[F, D]
  ): Field[F, QueryContext[A], D] = {
    val tfa: TableFieldAttribute[G, A, B, C, Query.Select[B]] = new TableFieldAttribute[G, A, B, C, Query.Select[B]] {
      def arg = a
      def query(value: A, argument: C): Query[G, Query.Select[B]] = f(value, argument)
      def fieldVariant = FieldVariant.Selection()
    }

    build
      .from(
        Resolver
          .id[F, QueryContext[A]]
          .emap { qa =>
            qa.read(tfa).toRight("internal query association error, could not read result from query result").flatten.toIor
          }
          .andThen(resolverCont)
      )(tpe)
      .addAttributes(tfa)
  }

  def contFull[F[_], G[_], A, B, C](a: EmptyableArg[C])(f: (A, C) => Query[G, B])(implicit
      tpe: => Out[F, G[QueryContext[B]]]
  ): Field[F, QueryContext[A], G[QueryContext[B]]] = {
    def addArg[I2] = a match {
      case EmptyableArg.Empty   => Resolver.id[F, I2]
      case EmptyableArg.Lift(y) => Resolver.id[F, I2].arg(y).map { case (_, i2) => i2 }
    }

    val tfa: TableFieldAttribute[G, A, QueryContext[B], C, B] =
      new TableFieldAttribute[G, A, QueryContext[B], C, B] {
        def arg = a
        def query(value: A, argument: C): Query[G, B] = f(value, argument)
        def fieldVariant = FieldVariant.SubSelection()
      }

    build
      .from(Resolver.id[F, QueryContext[A]].andThen(addArg).emap { qa =>
        qa.read(tfa).toRight("internal query association error, could not read result from query result").flatten.toIor
      })(tpe)
      .addAttributes(tfa)
  }

  def contVariant[F[_], A, B](f: A => Query[Option, B])(implicit tpe: => Type[F, QueryContext[B]]) = {
    val attr = new UnificationQueryAttribute[A, B, QueryContext[B]] {
      def fieldVariant: FieldVariant[B, QueryContext[B]] = FieldVariant.SubSelection[B]()
      def query(value: A): Query[Option, B] = f(value)
    }
    gql.ast.Variant[F, QueryContext[A], QueryContext[B]](Eval.always(tpe), List(attr)) { qr =>
      qr.read(attr).sequence.map(_.flatten).toIor
    }
  }

  def queryVariant[F[_], A, B](f: A => Query[Option, Query.Select[B]])(implicit tpe: => Type[F, B]) = {
    val attr = new UnificationQueryAttribute[A, Query.Select[B], B] {
      def fieldVariant: FieldVariant[Query.Select[B], B] = FieldVariant.Selection[B]()
      def query(value: A): Query[Option, Query.Select[B]] = f(value)
    }
    gql.ast.Variant[F, QueryContext[A], B](Eval.always(tpe), List(attr)) { qr =>
      qr.read(attr).sequence.map(_.flatten).toIor
    }
  }

  final class PartiallyAppliedRelationalUnion0[F[_], A](private val name: String) {
    def contVariant[B](f: A => Query[Option, B])(implicit tpe: => Type[F, QueryContext[B]]) =
      new PartiallyAppliedRelationalUnion1(name, self.contVariant[F, A, B](f)(tpe))

    def queryVariant[B](f: A => Query[Option, Query.Select[B]])(implicit tpe: => Type[F, B]) =
      new PartiallyAppliedRelationalUnion1(name, self.queryVariant[F, A, B](f)(tpe))
  }

  final class PartiallyAppliedRelationalUnion1[F[_], A](private val name: String, hd: Variant[F, QueryContext[A], ?]) {
    def contVariant[B](f: A => Query[Option, B])(implicit tpe: => Type[F, QueryContext[B]]) =
      Union[F, QueryContext[A]](name, NonEmptyList.of(hd, self.contVariant[F, A, B](f)(tpe)))

    def queryVariant[B](f: A => Query[Option, Query.Select[B]])(implicit tpe: => Type[F, B]) =
      Union[F, QueryContext[A]](name, NonEmptyList.of(hd, self.queryVariant[F, A, B](f)(tpe)))
  }

  final class RelationalUnionOps[F[_], A](private val u: Union[F, QueryContext[A]]) {
    def contVariant[B](f: A => Query[Option, B])(implicit tpe: => Type[F, QueryContext[B]]): Union[F, QueryContext[A]] =
      u.copy(types = u.types :+ self.contVariant[F, A, B](f)(tpe))

    def queryVariant[B](f: A => Query[Option, Query.Select[B]])(implicit tpe: => Type[F, B]): Union[F, QueryContext[A]] =
      u.copy(types = u.types :+ self.queryVariant[F, A, B](f)(tpe))
  }

  implicit def relationalUnionDslOps[F[_], A](u: Union[F, QueryContext[A]]): RelationalUnionOps[F, A] =
    new RelationalUnionOps(u)

  def contImplementation[F[_], A, B](f: B => Query[Option, A])(implicit interface: => Interface[F, QueryContext[B]]) = {
    val attr = new UnificationQueryAttribute[B, A, QueryContext[A]] {
      def fieldVariant: FieldVariant[A, QueryContext[A]] = FieldVariant.SubSelection[A]()
      def query(value: B): Query[Option, A] = f(value)
    }
    gql.ast.Implementation[F, QueryContext[A], QueryContext[B]](Eval.always(interface), List(attr)) { qr =>
      qr.read(attr).sequence.map(_.flatten).toIor
    }
  }

  def queryImplementation[F[_], A, B](f: B => Query[Option, Query.Select[A]])(implicit interface: => Interface[F, QueryContext[B]]) = {
    val attr = new UnificationQueryAttribute[B, Query.Select[A], A] {
      def fieldVariant: FieldVariant[Query.Select[A], A] = FieldVariant.Selection[A]()
      def query(value: B): Query[Option, Query.Select[A]] = f(value)
    }
    gql.ast.Implementation[F, A, QueryContext[B]](Eval.always(interface), List(attr)) { qr =>
      qr.read(attr).sequence.map(_.flatten).toIor
    }
  }

  final class RelationalTypeOps1[F[_], A](private val tpe: Type[F, QueryContext[A]]) {
    def contImplements[B](f: B => Query[Option, A])(implicit interface: => Interface[F, QueryContext[B]]): Type[F, QueryContext[A]] =
      tpe.copy(implementations = self.contImplementation[F, A, B](f)(interface) :: tpe.implementations)
  }

  implicit def relationalTypeDslOps1[F[_], A](tpe: Type[F, QueryContext[A]]): RelationalTypeOps1[F, A] =
    new RelationalTypeOps1(tpe)

  final class RelationalTypeOps0[F[_], A](private val tpe: Type[F, A]) {
    def queryImplements[B](f: B => Query[Option, Query.Select[A]])(implicit interface: => Interface[F, QueryContext[B]]): Type[F, A] =
      tpe.copy(implementations = self.queryImplementation[F, A, B](f)(interface) :: tpe.implementations)
  }

  implicit def relationalTypeDslOps0[F[_], A](tpe: Type[F, A]): RelationalTypeOps0[F, A] =
    new RelationalTypeOps0(tpe)

  final class RelationalFieldBuilder[F[_], A](@unused private val dummy: Boolean = false) {
    def tpe(
        name: String,
        hd: (String, Field[F, QueryContext[A], ?]),
        tl: (String, Field[F, QueryContext[A], ?])*
    ): Type[F, QueryContext[A]] =
      gql.dsl.all.tpe[F, QueryContext[A]](name, hd, tl: _*)

    def union(name: String) = new PartiallyAppliedRelationalUnion0[F, A](name)

    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, QueryContext[A], C] =
      queryFull[F, G, A, B, Unit, C](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, QueryContext[A], D] =
      queryFull[F, G, A, B, C, D](EmptyableArg.Lift(a))((a, c) => f(a, c), g(Resolver.id[F, G[B]]))(tpe)

    def contBoundaryFull[G[_]: Reassociateable, H[_], B, C, D, Arg1, Arg2](ea1: EmptyableArg[Arg1], connection: Connection[F])(
        f: (A, Arg1) => Query[G, Query.Select[B]]
    )(ea2: EmptyableArg[Arg2])(continue: (NonEmptyList[B], Arg2) => Query[H, (Query.Select[B], C)])(implicit
        F: Applicative[F],
        Q: Queryable[F],
        tpe: => Out[F, G[H[QueryContext[C]]]]
    ) =
      queryFull[F, G, A, B, Arg1, G[H[QueryContext[C]]]](ea1)(
        f,
        Resolver
          .id[F, G[B]]
          .andThen(
            resolveQueryFull[F, H, G, B, C, Arg2](ea2, continue, connection)
          )
      )(tpe)

    def contBoundary[G[_]: Reassociateable, H[_], B, C, D, ArgType](a: Arg[ArgType], connection: Connection[F])(
        f: (A, ArgType) => Query[G, Query.Select[B]]
    )(continue: (NonEmptyList[B], ArgType) => Query[H, (Query.Select[B], C)])(implicit
        F: Applicative[F],
        Q: Queryable[F],
        tpe: => Out[F, G[H[QueryContext[C]]]]
    ) =
      contBoundaryFull[G, H, B, C, D, ArgType, ArgType](EmptyableArg.Lift(a), connection)(f)(EmptyableArg.Lift(a))(continue)(
        implicitly,
        implicitly,
        implicitly,
        tpe
      )

    def contBoundary[G[_]: Reassociateable, H[_], B, C, D](connection: Connection[F])(
        f: A => Query[G, Query.Select[B]]
    )(continue: NonEmptyList[B] => Query[H, (Query.Select[B], C)])(implicit
        F: Applicative[F],
        Q: Queryable[F],
        tpe: => Out[F, G[H[QueryContext[C]]]]
    ) =
      contBoundaryFull[G, H, B, C, D, Unit, Unit](EmptyableArg.Empty, connection)((i, _) => f(i))(EmptyableArg.Empty)((i, _) =>
        continue(i)
      )(
        implicitly,
        implicitly,
        implicitly,
        tpe
      )

    def query[G[_], B](f: A => Query[G, Query.Select[B]])(implicit
        tpe: => Out[F, G[B]]
    ): Field[F, QueryContext[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, QueryContext[A], G[B]] =
      self.query(a)(f)(tpe)

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

    def cont[G[_], B, C](a: Arg[C])(f: (A, C) => Query[G, B])(implicit
        tpe: => Out[F, G[QueryContext[B]]]
    ): Field[F, QueryContext[A], G[QueryContext[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[QueryContext[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[QueryContext[B]]]) =
      self.runField(connection)(q)(Q, F, tpe)

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

    def runFieldSingle[G[_], I, B](connection: Connection[F])(
        q: I => Query[G, B]
    )(implicit F: Applicative[F], Q: Queryable[F], tpe: => Out[F, G[QueryContext[B]]]) =
      self.runFieldSingle(connection)(q)(Q, F, tpe)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy