Packages

Maple is an automatic, compile time, client code generator for GraphQL schemas. At best it creates easy to use API functions for use in your code. At worst it can be used as a CLI for a GraphQL API.

Current section

Files

Jump to
maple lib maple.ex
Raw

lib/maple.ex

defmodule Maple do
@moduledoc """
The purpose of this module is to parse a GraphQL schema and to dynamically create
easy to use client code functions at compile time with which a user can execute queries and
mutations on a GraphQL endpoint.
The module takes a Keyword list with the following options:
- `:build_type_structs` - Default is `false`. If set to `true` the macro will create
structs for all the fields found in the introspection query. All types are namespaced into
`Maple.Types.`
- `:http_adapter` - The default HTTP adapter for completing transactions against the GraphQL
server. Default is: `:"Elixir.Maple.Clients.Http"`
- `:websocket_adapter` - The default Websocket adapter for completing transactions against the GraphQL
server using websockets. Default is: `:"Elixir.Maple.Clients.WebsocketApollo"`
"""
@default_options [
build_type_structs: false,
http_adapter: :"Elixir.Maple.Clients.Http",
websocket_adapter: :"Elixir.Maple.Clients.WebsocketApollo"
]
@known_types ~w(
__Directive
__DirectiveLocation
__EnumValue
__Field
__InputValue
__Schema
__Type
Boolean
Float
ID
Int
String
)
alias Maple.Helpers
defmacro __using__(_vars) do
quote do
import Maple
end
end
defmacro generate_graphql_functions(options \\ []) do
options = Keyword.merge(@default_options, options)
schema =
options[:http_adapter]
|> apply(:schema, [])
|> Map.get("__schema")
mutation_type_name = schema["mutationType"]["name"]
query_type_name = schema["queryType"]["name"]
subscription_type_name = schema["subscriptionType"]["name"]
schema["types"]
|> Enum.reduce([], fn type, acc ->
cond do
type["name"] == mutation_type_name ->
#Create mutation functions
acc ++
Enum.map(type["fields"], fn func ->
function = Helpers.assign_function_params(func)
Helpers.generate_mutation(function, options[:http_adapter])
end)
type["name"] == query_type_name ->
#Create query functions
acc ++
Enum.map(type["fields"], fn func ->
function = Helpers.assign_function_params(func)
# Check if we can create a query function with /1 when there are no required params
if length(function[:required_params]) == 0 do
[
Helpers.generate_one_arity_query(function, options[:http_adapter]),
Helpers.generate_two_arity_query(function, options[:http_adapter])
]
else
[Helpers.generate_two_arity_query(function, options[:http_adapter])]
end
end)
type["name"] == subscription_type_name ->
#Create subscription functions
acc ++
Enum.map(type["fields"], fn func ->
function = Helpers.assign_function_params(func)
Helpers.generate_subscription(function, options[:websocket_adapter])
end)
!Enum.member?(@known_types, type["name"]) && type["fields"] && options[:build_type_structs] ->
# Create structs
acc ++
[quote do
defmodule unquote(Module.concat(["Maple", "Types", type["name"]])) do
defstruct Enum.map(unquote(Macro.escape(type["fields"])), &(String.to_atom(&1["name"])))
end
end]
true -> acc
end
end)
end
end