Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegIlyenko committed Aug 9, 2015
0 parents commit 349fbe5
Show file tree
Hide file tree
Showing 37 changed files with 11,772 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
@@ -0,0 +1,8 @@
logs
target
/.idea
/.idea_modules
/.classpath
/.project
/.settings
/RUNNING_PID
8 changes: 8 additions & 0 deletions LICENSE
@@ -0,0 +1,8 @@
This software is licensed under the Apache 2 license, quoted below.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project 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.
4 changes: 4 additions & 0 deletions README
@@ -0,0 +1,4 @@
This is your new Play application
=================================

This file will be packaged with your application, when using `activator dist`.
4 changes: 4 additions & 0 deletions activator.properties
@@ -0,0 +1,4 @@
name=sangria-playground
title=Sangria Playground Example
description=An example GraphQL server written with Play and sangria.
tags=sangria,graphql,playframework,scala,basics
60 changes: 60 additions & 0 deletions app/controllers/Application.scala
@@ -0,0 +1,60 @@
package controllers

import javax.inject.Inject

import akka.actor.ActorSystem
import play.api._
import play.api.libs.json._
import play.api.mvc._

import sangria.execution.Executor
import sangria.introspection.introspectionQuery
import sangria.parser.{SyntaxError, QueryParser}
import sangria.integration.PlayJsonSupport._

import models.{FriendsResolver, CharacterRepo, SchemaDefinition}
import sangria.renderer.SchemaRenderer

import scala.concurrent.Future
import scala.util.{Failure, Success}

class Application @Inject() (system: ActorSystem) extends Controller {
import system.dispatcher

def index = Action {
Ok(views.html.index())
}

val executor = Executor(
schema = SchemaDefinition.StarWarsSchema,
userContext = new CharacterRepo,
deferredResolver = new FriendsResolver)

def graphql(query: String, args: Option[String], operation: Option[String]) = Action.async {
QueryParser.parse(query) match {

// query parsed successfully, time to execute it!
case Success(queryAst) =>
executor.execute(queryAst,
operationName = operation,
arguments = args map Json.parse) map (Ok(_))

// can't parse GraphQL query, return error
case Failure(error: SyntaxError) =>
Future.successful(BadRequest(Json.obj(
"syntaxError" -> error.getMessage,
"locations" -> Json.arr(Json.obj(
"line" -> error.originalError.position.line,
"column" -> error.originalError.position.column)))))

case Failure(error) =>
throw error
}
}

def renderSchema = Action.async {
executor.execute(introspectionQuery) map (res =>
SchemaRenderer.renderSchema(res) map (Ok(_)) getOrElse
InternalServerError("Can't render the schema!"))
}
}
110 changes: 110 additions & 0 deletions app/models/Data.scala
@@ -0,0 +1,110 @@
package models

import sangria.schema.{Deferred, DeferredResolver}

import scala.concurrent.Future
import scala.util.Try

object Episode extends Enumeration {
val NEWHOPE, EMPIRE, JEDI = Value
}

trait Character {
def id: String
def name: Option[String]
def friends: List[String]
def appearsIn: List[Episode.Value]
}

case class Human(
id: String,
name: Option[String],
friends: List[String],
appearsIn: List[Episode.Value],
homePlanet: Option[String]) extends Character

case class Droid(
id: String,
name: Option[String],
friends: List[String],
appearsIn: List[Episode.Value],
primaryFunction: Option[String]) extends Character

/**
* Instructs sangria to postpone the expansion of the friends list to the last responsible moment and then batch
* all collected defers together.
*/
case class DeferFriends(friends: List[String]) extends Deferred[List[Option[Character]]]

/**
* Resolves the lists of friends collected during the query execution.
* For this demonstration the implementation is pretty simplistic, but in real-world scenario you
* probably want to batch all of the deferred values in one efficient fetch.
*/
class FriendsResolver extends DeferredResolver {
override def resolve(deferred: List[Deferred[Any]]) = Future.fromTry(Try(deferred map {
case DeferFriends(friendIds) =>
friendIds map (id => CharacterRepo.humans.find(_.id == id) orElse CharacterRepo.droids.find(_.id == id))
}))
}

class CharacterRepo {
import models.CharacterRepo._

def getHero(episode: Option[Episode.Value]) =
episode flatMap (_ => getHuman("1000")) getOrElse droids.last

def getHuman(id: String): Option[Human] = humans.find(c => c.id == id)

def getDroid(id: String): Option[Droid] = droids.find(c => c.id == id)
}

object CharacterRepo {
val humans = List(
Human(
id = "1000",
name = Some("Luke Skywalker"),
friends = List("1002", "1003", "2000", "2001"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
homePlanet = Some("Tatooine")),
Human(
id = "1001",
name = Some("Darth Vader"),
friends = List("1004"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
homePlanet = Some("Tatooine")),
Human(
id = "1002",
name = Some("Han Solo"),
friends = List("1000", "1003", "2001"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
homePlanet = None),
Human(
id = "1003",
name = Some("Leia Organa"),
friends = List("1000", "1002", "2000", "2001"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
homePlanet = Some("Alderaan")),
Human(
id = "1004",
name = Some("Wilhuff Tarkin"),
friends = List("1001"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
homePlanet = None)
)

val droids = List(
Droid(
id = "2000",
name = Some("C-3PO"),
friends = List("1000", "1002", "1003", "2001"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
primaryFunction = Some("Protocol")),
Droid(
id = "2001",
name = Some("R2-D2"),
friends = List("1000", "1002", "1003"),
appearsIn = List(Episode.NEWHOPE, Episode.EMPIRE, Episode.JEDI),
primaryFunction = Some("Astromech"))
)
}
108 changes: 108 additions & 0 deletions app/models/SchemaDefinition.scala
@@ -0,0 +1,108 @@
package models

import sangria.schema._

import scala.concurrent.Future

/**
* Defines a GraphQL schema for the current project
*/
object SchemaDefinition {
val EpisodeEnum = EnumType(
"Episode",
Some("One of the films in the Star Wars Trilogy"),
List(
EnumValue("NEWHOPE",
value = Episode.NEWHOPE,
description = Some("Released in 1977.")),
EnumValue("EMPIRE",
value = Episode.EMPIRE,
description = Some("Released in 1980.")),
EnumValue("JEDI",
value = Episode.JEDI,
description = Some("Released in 1983."))))

val Character: InterfaceType[Unit, Character] =
InterfaceType(
"Character",
"A character in the Star Wars Trilogy",
() => List[Field[Unit, Character]](
Field("id", StringType,
Some("The id of the character."),
resolve = _.value.id),
Field("name", OptionType(StringType),
Some("The name of the character."),
resolve = _.value.name),
Field("friends", OptionType(ListType(OptionType(Character))),
Some("The friends of the character, or an empty list if they have none."),
resolve = ctx => DeferFriends(ctx.value.friends)),
Field("appearsIn", OptionType(ListType(OptionType(EpisodeEnum))),
Some("Which movies they appear in."),
resolve = _.value.appearsIn map (e => Some(e)))
))

val Human =
ObjectType[Unit, Human](
"Human",
"A humanoid creature in the Star Wars universe.",
List[Field[Unit, Human]](
Field("id", StringType,
Some("The id of the human."),
resolve = _.value.id),
Field("name", OptionType(StringType),
Some("The name of the human."),
resolve = _.value.name),
Field("friends", OptionType(ListType(OptionType(Character))),
Some("The friends of the human, or an empty list if they have none."),
resolve = (ctx) => DeferFriends(ctx.value.friends)),
Field("appearsIn", OptionType(ListType(OptionType(EpisodeEnum))),
Some("Which movies they appear in."),
resolve = _.value.appearsIn map (e => Some(e))),
Field("homePlanet", OptionType(StringType),
Some("The home planet of the human, or null if unknown."),
resolve = _.value.homePlanet)
),
Character :: Nil)

val Droid = ObjectType[Unit, Droid](
"Droid",
"A mechanical creature in the Star Wars universe.",
List[Field[Unit, Droid]](
Field("id", StringType,
Some("The id of the droid."),
resolve = Projection("_id", _.value.id)),
Field("name", OptionType(StringType),
Some("The name of the droid."),
resolve = ctx => Future.successful(ctx.value.name)),
Field("friends", OptionType(ListType(OptionType(Character))),
Some("The friends of the droid, or an empty list if they have none."),
resolve = ctx => DeferFriends(ctx.value.friends)),
Field("appearsIn", OptionType(ListType(OptionType(EpisodeEnum))),
Some("Which movies they appear in."),
resolve = _.value.appearsIn map (e => Some(e))),
Field("primaryFunction", OptionType(StringType),
Some("The primary function of the droid."),
resolve = _.value.primaryFunction)
),
Character :: Nil)

val ID = Argument("id", StringType, description = "id of the character")

val EpisodeArg = Argument("episode", OptionInputType(EpisodeEnum),
description = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.")

val Query = ObjectType[CharacterRepo, Unit](
"Query", List[Field[CharacterRepo, Unit]](
Field("hero", Character,
arguments = EpisodeArg :: Nil,
resolve = (ctx) => ctx.ctx.getHero(ctx.argOpt(EpisodeArg))),
Field("human", OptionType(Human),
arguments = ID :: Nil,
resolve = ctx => ctx.ctx.getHuman(ctx arg ID)),
Field("droid", Droid,
arguments = ID :: Nil,
resolve = Projector((ctx, f) => ctx.ctx.getDroid(ctx arg ID).get))
))

val StarWarsSchema = Schema(Query)
}

0 comments on commit 349fbe5

Please sign in to comment.