Created
June 26, 2018 08:51
-
-
Save marioosh/316468a9ac5e1179e226a3191f03fab8 to your computer and use it in GitHub Desktop.
HowToGraphql - Sangria tutorial - Mutations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.howtographql.scala.sangria | |
import DBSchema._ | |
import com.howtographql.scala.sangria.models.{AuthProviderSignupData, Link, User, Vote} | |
import sangria.execution.deferred.{RelationIds, SimpleRelation} | |
import slick.jdbc.H2Profile.api._ | |
import scala.concurrent.Future | |
class DAO(db: Database) { | |
def allLinks = db.run(Links.result) | |
def getLinks(ids: Seq[Int]): Future[Seq[Link]] = db.run( | |
Links.filter(_.id inSet ids).result | |
) | |
def getLinksByUserIds(ids: Seq[Int]): Future[Seq[Link]] = { | |
db.run { | |
Links.filter(_.postedBy inSet ids).result | |
} | |
} | |
def getUsers(ids: Seq[Int]): Future[Seq[User]] = db.run( | |
Users.filter(_.id inSet ids).result | |
) | |
def getVotes(ids: Seq[Int]): Future[Seq[Vote]] = { | |
db.run( | |
Votes.filter(_.id inSet ids).result | |
) | |
} | |
def getVotesByRelationIds(rel: RelationIds[Vote]): Future[Seq[Vote]] = | |
db.run( | |
Votes.filter { vote => | |
rel.rawIds.collect({ | |
case (SimpleRelation("byUser"), ids: Seq[Int]) => vote.userId inSet ids | |
case (SimpleRelation("byLink"), ids: Seq[Int]) => vote.linkId inSet ids | |
}).foldLeft(true: Rep[Boolean])(_ || _) | |
} result | |
) | |
def createUser(name: String, authProvider: AuthProviderSignupData): Future[User] = { | |
val newUser = User(0, name, authProvider.email.email, authProvider.email.password ) | |
val insertAndReturnUserQuery = (Users returning Users.map(_.id)) into { | |
(user, id) => user.copy(id = id) | |
} | |
db.run { | |
insertAndReturnUserQuery += newUser | |
} | |
} | |
def createLink(url: String, description: String, postedBy: Int): Future[Link] = { | |
val insertAndReturnLinkQuery = (Links returning Links.map(_.id)) into { | |
(link, id) => link.copy(id = id) | |
} | |
db.run { | |
insertAndReturnLinkQuery += Link(0, url, description, postedBy) | |
} | |
} | |
def createVote(linkId: Int, userId: Int): Future[Vote] = { | |
val insertAndReturnVoteQuery = (Votes returning Votes.map(_.id)) into { | |
(vote, id) => vote.copy(id = id) | |
} | |
db.run { | |
insertAndReturnVoteQuery += Vote(0, userId, linkId) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.howtographql.scala.sangria | |
import akka.http.scaladsl.model.DateTime | |
import sangria.schema.{ListType, ObjectType} | |
import models._ | |
import sangria.ast.StringValue | |
import sangria.execution.deferred.{DeferredResolver, Fetcher, Relation, RelationIds} | |
import sangria.schema._ | |
import sangria.macros.derive._ | |
object GraphQLSchema { | |
implicit val GraphQLDateTime = ScalarType[DateTime](//1 | |
"DateTime",//2 | |
coerceOutput = (dt, _) => dt.toString, //3 | |
coerceInput = { //4 | |
case StringValue(dt, _, _ ) => DateTime.fromIsoDateTimeString(dt).toRight(DateTimeCoerceViolation) | |
case _ => Left(DateTimeCoerceViolation) | |
}, | |
coerceUserInput = { //5 | |
case s: String => DateTime.fromIsoDateTimeString(s).toRight(DateTimeCoerceViolation) | |
case _ => Left(DateTimeCoerceViolation) | |
} | |
) | |
val IdentifiableType = InterfaceType( | |
"Identifiable", | |
fields[Unit, Identifiable]( | |
Field("id", IntType, resolve = _.value.id) | |
) | |
) | |
lazy val LinkType: ObjectType[Unit, Link] = deriveObjectType[Unit, Link]( | |
Interfaces(IdentifiableType), | |
ReplaceField("createdAt", Field("createdAt", GraphQLDateTime, resolve = _.value.createdAt)), | |
ReplaceField("postedBy", | |
Field("postedBy", UserType, resolve = c => usersFetcher.defer(c.value.postedBy)) | |
), | |
AddFields( | |
Field("votes", ListType(VoteType), resolve = c => votesFetcher.deferRelSeq(voteByLinkRel, c.value.id)) | |
) | |
) | |
lazy val UserType: ObjectType[Unit, User] = deriveObjectType[Unit, User]( | |
Interfaces(IdentifiableType), | |
AddFields( | |
Field("links", ListType(LinkType), | |
resolve = c => linksFetcher.deferRelSeq(linkByUserRel, c.value.id)), | |
Field("votes", ListType(VoteType), | |
resolve = c => votesFetcher.deferRelSeq(voteByUserRel, c.value.id)) | |
) | |
) | |
lazy val VoteType: ObjectType[Unit, Vote] = deriveObjectType[Unit, Vote]( | |
Interfaces(IdentifiableType), | |
ExcludeFields("userId", "linkId"), | |
AddFields(Field("user", UserType, resolve = c => usersFetcher.defer(c.value.userId))), | |
AddFields(Field("link", LinkType, resolve = c => linksFetcher.defer(c.value.linkId))) | |
) | |
import sangria.marshalling.sprayJson._ | |
import spray.json.DefaultJsonProtocol._ | |
implicit val authProviderEmailFormat = jsonFormat2(AuthProviderEmail) | |
implicit val authProviderSignupDataFormat = jsonFormat1(AuthProviderSignupData) | |
implicit val AuthProviderEmailInputType: InputObjectType[AuthProviderEmail] = deriveInputObjectType[AuthProviderEmail]( | |
InputObjectTypeName("AUTH_PROVIDER_EMAIL") | |
) | |
lazy val AuthProviderSignupDataInputType: InputObjectType[AuthProviderSignupData] = deriveInputObjectType[AuthProviderSignupData]() | |
val linkByUserRel = Relation[Link, Int]("byUser", l => Seq(l.postedBy)) | |
val voteByLinkRel = Relation[Vote, Int]("byLink", v => Seq(v.linkId)) | |
val voteByUserRel = Relation[Vote, Int]("byUser", v => Seq(v.userId)) | |
val linksFetcher = Fetcher.rel( | |
(ctx: MyContext, ids: Seq[Int]) => ctx.dao.getLinks(ids), | |
(ctx: MyContext, ids: RelationIds[Link]) => ctx.dao.getLinksByUserIds(ids(linkByUserRel)) | |
) | |
val usersFetcher = Fetcher( | |
(ctx: MyContext, ids: Seq[Int]) => ctx.dao.getUsers(ids) | |
) | |
val votesFetcher = Fetcher.rel( | |
(ctx: MyContext, ids: Seq[Int]) => ctx.dao.getVotes(ids), | |
(ctx: MyContext, ids: RelationIds[Vote]) => ctx.dao.getVotesByRelationIds(ids) | |
) | |
val Resolver = DeferredResolver.fetchers(linksFetcher, usersFetcher, votesFetcher) | |
val Id = Argument("id", IntType) | |
val Ids = Argument("ids", ListInputType(IntType)) | |
val QueryType = ObjectType( | |
"Query", | |
fields[MyContext, Unit]( | |
Field("allLinks", ListType(LinkType), resolve = c => c.ctx.dao.allLinks), | |
Field("link", | |
OptionType(LinkType), | |
arguments = Id :: Nil, | |
resolve = c => linksFetcher.deferOpt(c.arg(Id)) | |
), | |
Field("links", | |
ListType(LinkType), | |
arguments = Ids :: Nil, | |
resolve = c => linksFetcher.deferSeq(c.arg(Ids)) | |
), | |
Field("users", | |
ListType(UserType), | |
arguments = List(Ids), | |
resolve = c => usersFetcher.deferSeq(c.arg(Ids)) | |
), | |
Field("votes", | |
ListType(VoteType), | |
arguments = List(Ids), | |
resolve = c => votesFetcher.deferSeq(c.arg(Ids)) | |
) | |
) | |
) | |
val NameArg = Argument("name", StringType) | |
val AuthProviderArg = Argument("authProvider", AuthProviderSignupDataInputType) | |
val UrlArg = Argument("url", StringType) | |
val DescArg = Argument("description", StringType) | |
val PostedByArg = Argument("postedById", IntType) | |
val LinkIdArg = Argument("linkId", IntType) | |
val UserIdArg = Argument("userId", IntType) | |
val Mutation = ObjectType( | |
"Mutation", | |
fields[MyContext, Unit]( | |
Field("createUser", | |
UserType, | |
arguments = NameArg :: AuthProviderArg :: Nil, | |
resolve = c => c.ctx.dao.createUser(c.arg(NameArg), c.arg(AuthProviderArg)) | |
), | |
Field("createLink", | |
LinkType, | |
arguments = UrlArg :: DescArg :: PostedByArg :: Nil, | |
resolve = c => c.ctx.dao.createLink(c.arg(UrlArg), c.arg(DescArg), c.arg(PostedByArg))), | |
Field("createVote", | |
VoteType, | |
arguments = LinkIdArg :: UserIdArg :: Nil, | |
resolve = c => c.ctx.dao.createVote(c.arg(LinkIdArg), c.arg(UserIdArg))) | |
) | |
) | |
val SchemaDefinition = Schema(QueryType, Some(Mutation)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.howtographql.scala.sangria | |
import akka.http.scaladsl.model.DateTime | |
import sangria.execution.deferred.HasId | |
import sangria.validation.Violation | |
package object models { | |
trait Identifiable { | |
val id: Int | |
} | |
object Identifiable { | |
implicit def hasId[T <: Identifiable]: HasId[T, Int] = HasId(_.id) | |
} | |
case class Link(id: Int, url: String, description: String, postedBy: Int, createdAt: DateTime = DateTime.now) extends Identifiable | |
case object DateTimeCoerceViolation extends Violation { | |
override def errorMessage: String = "Error during parsing DateTime" | |
} | |
case class User(id: Int, name: String, email: String, password: String, createdAt: DateTime = DateTime.now) extends Identifiable | |
case class Vote(id: Int, userId: Int, linkId: Int, createdAt: DateTime = DateTime.now) extends Identifiable | |
case class AuthProviderEmail(email: String, password: String) | |
case class AuthProviderSignupData(email: AuthProviderEmail) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment