Created
December 18, 2020 14:37
-
-
Save nickhudkins/8532c936f273d1b1353dacaeed7c7890 to your computer and use it in GitHub Desktop.
Relationship Fetchers
This file contains hidden or 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
import sangria.execution.deferred.{Fetcher, HasId, Relation, RelationIds, SimpleRelation} | |
object Fetchers { | |
/* | |
The following three methods are intended to be representative | |
of whatever your DAO may provide. | |
*/ | |
def recipesForUsers(ids: Seq[Id[User]]): Future[Seq[Recipe]] = Future { | |
Recipe(id = 3, foodId = 2, userId = 3) :: Nil | |
} | |
def recipesForFoods(ids: Seq[Id[Food]]): Future[Seq[Recipe]] = Future { | |
Recipe(id = 2, foodId = 5, userId = 6) :: Nil | |
} | |
def recipesById(ids: Seq[Id[Recipe]]): Future[Seq[Recipe]] = Future { | |
Recipe(id = 1, foodId = 7, userId = 8) :: Nil | |
} | |
/* | |
Here's where the fun begins! Skip reading this for now | |
*/ | |
// TODO: Can we do better? Can we NOT end up with type erasure? Maybe! | |
def recipesByRelation(ctx: MFPContext, value: RelationIds[Recipe]): Future[Seq[Recipe]] = { | |
val allRelations = value.rawIds.collect({ | |
case (Recipe.UserRel, ids: Seq[Id[User]] @unchecked) => recipesForUsers(ids) | |
case (Recipe.FoodRel, ids: Seq[Id[Food]] @unchecked) => recipesForFoods(ids) | |
}) | |
// It is possible that within a single fetch, we look things up by many different | |
// relations. Here we smush them all back together as though we fetched them as one. | |
Future.sequence(allRelations).map(_.flatten.toSeq) | |
} | |
val recipes = Fetcher.rel( | |
// By ID | |
(ctx: SomeContext, ids: Seq[Id[Recipe]]) => recipesById(ids), | |
// Relation Fetcher, take note of `RelationIds[Recipe]` which is a type | |
// that contains ALL relations defined for `Recipe` (see companion object of Recipe in models.scala) | |
(ctx: SomeContext, ids: RelationIds[Recipe]) => recipesByRelation(ctx, ids) | |
)(HasId(_.RecipeId)) | |
// if you add a new fetcher it must be in this list | |
val All = List( | |
recipes, | |
) | |
} |
This file contains hidden or 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
import sangria.execution.deferred.{Relation, SimpleRelation} | |
case class User(id: Int) | |
case class Food(id: Int) | |
case class Recipe(id: Int, userId: Int, foodId: Int) { | |
val RecipeId = Id[Recipe](id) | |
val UserId = Id[User](userId) | |
val FoodId = Id[Food](foodId) | |
} | |
object Recipe { | |
private val USER_REL = "byUser" | |
private val FOOD_REL = "byFood" | |
val byUser = Relation[Recipe, Id[User]](USER_REL, c => Seq(c.UserId)) | |
val byFood = Relation[Recipe, Id[Food]](FOOD_REL, c => Seq(c.FoodId)) | |
val UserRel = SimpleRelation(USER_REL) | |
val FoodRel = SimpleRelation(FOOD_REL) | |
} |
This file contains hidden or 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
object SchemaDefinition { | |
val RecipeType = deriveObjectType[Context, Recipe]() | |
val Query = ObjectType( | |
"Query", | |
fields[Context, Unit]( | |
Field( | |
"recipe", | |
OptionType(RecipeType), | |
// Normal | |
resolve = _ => Fetchers.recipes.deferOpt(Id[Recipe](1)) | |
), | |
Field( | |
"recipeByFood", | |
ListType(RecipeType), | |
// The Magic! .deferRel / .deferRelSeq (1-to-1, 1-to-many) respectively | |
// Note the first arg is a `Relation` | |
resolve = _ => Fetchers.recipes.deferRelSeq(Recipe.byFood, Id[Food](5)) | |
), | |
Field( | |
"recipeByUser", | |
ListType(RecipeType), | |
// The Magic! .deferRel / .deferRelSeq (1-to-1, 1-to-many) respectively | |
// Note the first arg is a `Relation` | |
resolve = _ => Fetchers.recipes.deferRelSeq(Recipe.byUser, Id[User](3)) | |
) | |
) | |
) | |
val Definition = Schema(Query) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment