haskell-graphql / graphql-api

Write type-safe GraphQL services in Haskell
BSD 3-Clause "New" or "Revised" License
406 stars 35 forks source link

Hydrating a GraphQL Object with a parent GraphQL Object vs API call? #189

Closed EdmundsEcho closed 6 years ago

EdmundsEcho commented 6 years ago

The conceptual question is: What is the best way to hydrate the query tree while allowing the end-user to sub-select various branches "along the way" given:

Here is the intended query:

{    
   records ( id:"xxx" ) {
        id   -- uid
        measurements ( type:"xx" ) {
            type   -- uid
            components ( names: (Maybe (List Text) ) {   << Resolver in question
                                  compName   -- uid
                                  compValues
         }
   }
}

This following is a Field that will be added to my list of Root queries:

-- Root Field for Components
type CompsRoot =
  Argument "parent"     (**TBD**)  :>
  Argument "names"      (Maybe (List Text)) :>
  Field    "components" (List Component)

What should I use as a parent (given the parent needs to be a single Measurement)

By definition of this question, I'm clearly missing something in how I'm thinking about this. Any guidance would be greatly appreciated.

- E

jml commented 6 years ago

Hi,

I'm afraid there's a lot I don't understand about this question.

A few suggestions:

Leaving this open in case others have insight.

jml

On Tue, 3 Jul 2018 at 14:48 EdmundsEcho notifications@github.com wrote:

Here is the intended query:

{ records ( id:"xxx" ) { measurements ( type:"xx" ) { type components ( names: (Maybe (List Text) ) { << Resolver in question compName compValues } } }

This following is a Field that will be added to my list of Root queries:

-- Root Field for Components type CompsRoot = Argument "parent" (TBD) :> Argument "names" (Maybe (List Text)) :> Field "components" (List Component)

What should I use as a parent (given the parent needs to be a single Measurement)

-

Option 1: API.Measurements + MeaKey = Components (I'm hosting the data in Data.Map)

  • task: find a way to reference the MeaKey, perhaps using the GQL Measurement Object?
  • Option 2: GQL.Measurement Object has a Field "components" (List Components)

  • how would my resolver filter (List Components) using the Field "compName" Text?
    • should I be using a parent GQL Object to hydrate a child Object?

By definition of this question, I'm clearly missing something in how I'm thinking about this. Any guidance would be greatly appreciated.

  • E

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/haskell-graphql/graphql-api/issues/189, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHq6hecWAYbqwZEc6UDMtjh4Rr1gxXlks5uC3YrgaJpZM4VA_P7 .

EdmundsEcho commented 6 years ago

I appreciate your comments. Admittedly, I was incorrect in how I was thinking about it.

The data structure.

data ParentWithThings = ParentWithThings
  { parentID :: Text
  , ChildThings :: Map ChildKey ChildThing
  }  etc...

Here is the solution:

Step 1: Define a Field type that accepts the lookup key. Step 2: Define a resolver that requires initializing (aka partial application) with the collection being searched.

module Schema.Queries.ChildThing
  where

import qualified Schema.Types.ChildThing as T

-- | The Queries.ChildThing defines how to retrieve the Type.ChildThing
type ChildThing =
  Argument "name" Text :>
  Field "childThing" (Maybe T.ChildThing)

-- | Retrieve the requested T.ChildThing
resolver :: Lib.ChildThings   -- ^ source = Parent :: ParentWithThings
         -> Text              -- ^ source = User input
         -> Handler IO (Maybe T.ChildThing)

resolver o k =
  let k' = Lib.ChildKey k
  in case (lookupChildThings k' o) of
    Nothing -> pure Nothing
    Just o' -> pure . Just $ T.resolver k' o'

Step 3: Reference the Field query type in the Object definition for the parent.

module Schema.Types.ParentWithThings
  where

type ParentWithThings = Object "ParentWithThings" '[]   -- handlers
  '[ Field "ParentID" Text                              -- primitive, no handler
   , Q.ChildThing                                       -- resolver; field w Arg :: Text
   ]

-- Lib -> GraphQL
resolver :: Lib.ChildKey -> Lib.ChildThings -> Handler IO ParentWithThings
resolver key o = pure
    $ pure (unKey key)
  :<> Q.resolver  o          -- :: Field = the query for the ChildThing