dillonkearns / elm-graphql

Autogenerate type-safe GraphQL queries in Elm.

Home Page:https://package.elm-lang.org/packages/dillonkearns/elm-graphql/latest

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Generate input object helpers

gampleman opened this issue · comments

At work we have an InputObject.Extra module, that looks like this:

withTags : List String -> { a | tags : OptionalArgument (List String) } -> { a | tags : OptionalArgument (List String) }
withTags tags inp =
    { inp | tags = Present tags }


withName : String -> { a | name : OptionalArgument String } -> { a | name : OptionalArgument String }
withName name inp =
    { inp | name = Present name }


withVisibility : List Visibility -> { a | withVisibility : OptionalArgument (List Visibility) } -> { a | withVisibility : OptionalArgument (List Visibility) }
withVisibility vis inp =
    { inp | withVisibility = Present vis }


withSetData : (SetDataInAnalysisInput -> SetDataInAnalysisInput) -> { a | setData : OptionalArgument SetDataInAnalysisInput } -> { a | setData : OptionalArgument SetDataInAnalysisInput }
withSetData fn inp =
    { inp | setData = Present (InputObject.buildSetDataInAnalysisInput fn) }

and so on. This makes simple queries nicer:

Mutation.updateProject projectId (InputObject.withTags ["some-tag"]) Project.projectId

-- vs

Mutation.updateProject projectId (\inp -> { inp | tags = Present ["some-tag" }) Project.projectId

More complex queries are quite nice also with function composition:

InputObject.buildProjectsInput (InputObject.withVisibility [ Public ] >> InputObject.withTags [ "tag" ])

-- vs 

InputObject.buildProjectsInput (\inp -> { inp | visibility = Present [ Public ], tags = Public [ "tag" ] })

Complex nested queries can also be simplified:

InputObject.buildAnalysisInput (InputObject.withSetData (InputObject.withTags ["tag"]))

-- vs 

InputObject.buildAnalysisInput (\inp -> 
   {inp | 
       setData = Present (InputObject.buildSetDataInAnalysisInput (\sdiaInp -> 
            { sdiaInp | tags = Present ["tag"] } 
        ) 
   } 
)

The idea here is to generate these helpers automatically, so this more compact form of input can be used by anyone.

I'm definitely open to a pull request for some functionality like this if you're interested in working on it. It seems like a good idea overall.

One thing to consider is that I think it would need to be more flexible to allow for name collisions:

withVisibility : List Visibility -> { a | withVisibility : OptionalArgument (List Visibility) } -> { a | withVisibility : OptionalArgument (List Visibility) }

So something like this instead, since you could have fields in different types of Input Objects that have the same name but different types of values:

withVisibility : visibility -> { a | withVisibility : OptionalArgument visibility } -> { a | withVisibility : OptionalArgument visibility }

Also, I think Matt Griffith and Ryan Haskell-Glatz explored some different approaches to building input object values at some point, so it might be worth comparing notes with them. Ryan wrote a post about a few ideas: https://rhg.dev/blog/exploring-graphql-input/. And I think Matt ended up with a slightly different approach.