Created
June 26, 2018 07:46
-
-
Save marioosh/7c3ee5fed1238c5daf89a4459727f575 to your computer and use it in GitHub Desktop.
HowToGraphql - Sangria tutorial - Relations - finish
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.{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 | |
) | |
} |
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 java.sql.Timestamp | |
import akka.http.scaladsl.model.DateTime | |
import com.howtographql.scala.sangria.models._ | |
import slick.jdbc.H2Profile.api._ | |
import scala.concurrent.duration._ | |
import scala.concurrent.Await | |
import scala.language.postfixOps | |
object DBSchema { | |
implicit val dateTimeColumnType = MappedColumnType.base[DateTime, Timestamp]( | |
dt => new Timestamp(dt.clicks), | |
ts => DateTime(ts.getTime) | |
) | |
class LinksTable(tag: Tag) extends Table[Link](tag, "LINKS"){ | |
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc) | |
def url = column[String]("URL") | |
def description = column[String]("DESCRIPTION") | |
def postedBy = column[Int]("USER_ID") | |
def createdAt = column[DateTime]("CREATED_AT") | |
def * = (id, url, description, postedBy, createdAt).mapTo[Link] | |
def postedByFK = foreignKey("postedBy_FK", postedBy, Users)(_.id) | |
} | |
val Links = TableQuery[LinksTable] | |
class UsersTable(tag: Tag) extends Table[User](tag, "USERS"){ | |
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc) | |
def name = column[String]("NAME") | |
def email = column[String]("EMAIL") | |
def password = column[String]("PASSWORD") | |
def createdAt = column[DateTime]("CREATED_AT") | |
def * = (id, name, email, password, createdAt).mapTo[User] | |
} | |
val Users = TableQuery[UsersTable] | |
class VotesTable(tag: Tag) extends Table[Vote](tag, "VOTES"){ | |
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc) | |
def userId = column[Int]("USER_ID") | |
def linkId = column[Int]("LINK_ID") | |
def createdAt = column[DateTime]("CREATED_AT") | |
def * = (id, userId, linkId, createdAt).mapTo[Vote] | |
def userFK = foreignKey("user_FK", userId, Users)(_.id) | |
def linkFK = foreignKey("link_FK", linkId, Links)(_.id) | |
} | |
val Votes = TableQuery[VotesTable] | |
/** | |
* Load schema and populate sample data within this Sequence od DBActions | |
*/ | |
val databaseSetup = DBIO.seq( | |
Users.schema.create, | |
Links.schema.create, | |
Votes.schema.create, | |
Users forceInsertAll Seq( | |
User(1, "mario", "[email protected]", "s3cr3t"), | |
User(2, "Fred", "[email protected]", "wilmalove") | |
), | |
Links forceInsertAll Seq( | |
Link(1, "http://howtographql.com", "Awesome community driven GraphQL tutorial",1, DateTime(2017,9,12)), | |
Link(2, "http://graphql.org", "Official GraphQL web page",1, DateTime(2017,10,1)), | |
Link(3, "https://facebook.github.io/graphql/", "GraphQL specification",2, DateTime(2017,10,2)) | |
), | |
Votes forceInsertAll Seq( | |
Vote(1, 1, 1), | |
Vote(2, 1, 2), | |
Vote(3, 1, 3), | |
Vote(4, 2, 2), | |
) | |
) | |
def createDatabase: DAO = { | |
val db = Database.forConfig("h2mem") | |
Await.result(db.run(databaseSetup), 10 seconds) | |
new DAO(db) | |
} | |
} |
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))) | |
) | |
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 SchemaDefinition = Schema(QueryType) | |
} |
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment