diff --git a/build.sbt b/build.sbt index 2125f1d..07d290f 100644 --- a/build.sbt +++ b/build.sbt @@ -46,9 +46,10 @@ lazy val root = (project in file(".")) "org.http4s" %% "http4s-core" % "0.23.21", "org.http4s" %% "http4s-dsl" % "0.23.21", "org.http4s" %% "http4s-ember-server" % "0.23.21", - "org.sangria-graphql" %% "sangria" % "4.0.1", + "org.sangria-graphql" %% "sangria" % "4.1.1", "org.sangria-graphql" %% "sangria-circe" % "1.3.2", - "io.circe" %% "circe-core" % "0.14.5", + "org.sangria-graphql" %% "sangria-federated" % "0.8.1", + "io.circe" %% "circe-core" % "0.14.9", "io.circe" %% "circe-parser" % "0.14.5", "io.circe" %% "circe-generic" % "0.14.5", "io.circe" %% "circe-optics" % "0.14.1", diff --git a/src/main/scala/GraphQLServer.scala b/src/main/scala/GraphQLServer.scala index c386b96..42e150a 100644 --- a/src/main/scala/GraphQLServer.scala +++ b/src/main/scala/GraphQLServer.scala @@ -32,6 +32,7 @@ class GraphQLServer(documentRepo:DocumentRepo) { IO.fromFuture( IO { val context = GQLQueryContext(documentRepo, tier) + //FIXME - add in variables here Executor.execute(inUseSchema, doc, middleware = permissions :: metrics :: Nil, userContext = context).map(_.asJson.noSpaces) } ) diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 2f3b790..795a908 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -1,6 +1,6 @@ import cats.effect._ import com.comcast.ip4s.{IpLiteralSyntax, Ipv4Address, Ipv6Address} -import datastore.ElasticsearchRepo +import datastore.{ElasticsearchRepo, GQLQueryContext} import io.prometheus.client.hotspot.DefaultExports import org.http4s._ import org.http4s.dsl.io._ @@ -10,6 +10,10 @@ import org.http4s.implicits._ import org.slf4j.LoggerFactory import security.{ApiKeyAuth, DeveloperTier, InternalTier, Security, UserTier} import internalmetrics.PrometheusMetrics +import io.circe.Json +import org.http4s.headers.Server +import sangria.federation.v2.Federation +import sangria.schema.Schema import scala.concurrent.duration._ import utils.Config.fetchConfig @@ -64,6 +68,11 @@ object Main extends IOApp { } } +// private def graphQL[F[_]: Async]: GraphQL[F, GQLQueryContext] = { +// val (schema, um) = Federation.federate[GQLQueryContext, Any, Json]( +// Schema +// ) +// } def run(args:List[String]):IO[ExitCode] = { val httpApp = Router("/" -> graphqlService).orNotFound logger.info("Starting up on 0.0.0.0 port 9000") @@ -77,5 +86,10 @@ object Main extends IOApp { .build .use(_=>IO.never) .as(ExitCode.Success) +// Server.resource[IO, GQLQueryContext]( +// logger, +// graphQL, +// port"9000" +// ).use() } } diff --git a/src/main/scala/com/gu/contentapi/porter/graphql/Content.scala b/src/main/scala/com/gu/contentapi/porter/graphql/Content.scala index 44c76fb..581ead6 100644 --- a/src/main/scala/com/gu/contentapi/porter/graphql/Content.scala +++ b/src/main/scala/com/gu/contentapi/porter/graphql/Content.scala @@ -8,6 +8,8 @@ import io.circe.generic.auto._ import io.circe.syntax._ import com.gu.contentapi.porter.model import datastore.GQLQueryContext +import sangria.execution.deferred.HasId +import sangria.federation.v2.Directives import sangria.macros.derive import security.{InternalTier, RightsManagedTier} @@ -163,6 +165,7 @@ object Content { } ) ) - ) + ).withDirective(Directives.Key("id")) + implicit val ContentHasId:HasId[model.Content, String] = HasId(_.id) } diff --git a/src/main/scala/com/gu/contentapi/porter/graphql/RootQuery.scala b/src/main/scala/com/gu/contentapi/porter/graphql/RootQuery.scala index 8f41016..d930542 100644 --- a/src/main/scala/com/gu/contentapi/porter/graphql/RootQuery.scala +++ b/src/main/scala/com/gu/contentapi/porter/graphql/RootQuery.scala @@ -9,7 +9,10 @@ import io.circe.Json import scala.concurrent.ExecutionContext.Implicits.global import io.circe.generic.auto._ +import io.circe.generic.semiauto.deriveDecoder import org.slf4j.LoggerFactory +import sangria.execution.deferred.Fetcher +import sangria.federation.v2.{Directives, EntityResolver, Federation} import scala.concurrent.Future @@ -128,5 +131,25 @@ object RootQuery { ) ) - val schema = Schema(Query) + //For graphQL Federeation + import com.gu.contentapi.porter.graphql.Content.ContentHasId + case class StateArg(id: String) + val articles: Fetcher[GQLQueryContext, Content, Content, String] = Fetcher { + (ctx, ids) => Future.sequence( + ids.map(id=>ctx.repo.docById(id).map(_.map(contentTransform)) + .map(_.nodes.headOption)) + ).map(_.collect({case Some(content)=>content})) + } + + implicit val decoder: sangria.federation.v2.Decoder[Json, StateArg] = deriveDecoder[StateArg].decodeJson(_) + private val articleStateResolver = EntityResolver[GQLQueryContext, Json, Content, StateArg]( + __typeName = Content.getClass.getTypeName, + (arg, _) => articles.deferOpt(arg.id) + ) + + val (schema, um) = Federation.federate[GQLQueryContext, Unit, Json]( + Schema(Query), + sangria.marshalling.circe.CirceInputUnmarshaller, + articleStateResolver + ) }