# Absinthe Query All
Absinthe Query All is a tool for comprehensively and automatically querying and testing your Absinthe GraphQL schema.
You can read the story of how the core functionality of this package was developed [here](https://www.neurodynamic.online/writing/frontend-to-backend-type-safety).
This library primarily provides two utility functions.
## the `get_all_operations` function
This function returns a list of every operation of the specified type (i.e. query or mutation) in your absinthe graphql schema.
```elixir
AbsintheQueryAll.get_all_operations(SomeAbsintheSchema, :query)
[
:someQuery, :someOtherQuery, :someThirdQuery
]
```
## the `get_comprehensive_response` function
The `get_comprehensive_response` function runs the graphql operation specified, requesting a response that contains every possible field and subfield.
```elixir
AbsintheQueryAll.get_comprehensive_response(%{
schema: SomeAbsintheSchema,
operation_type: :query,
operation_name: :someOperation,
})
{:ok, %{data: %{"someField" => 1, "someOtherField" => 2, "someThirdField" => 3}}}
```
## Automatic comprehensiveness checking of your absinthe `resolve` callbacks
Why these two functions? So that you can set them up like this:
```elixir
describe "SomeAbsintheSchema" do
# First we get every single query available in the schema
for operation <- AbsintheQueryAll.get_all_operations(SomeAbsintheSchema, :query) do
# Then we create a test case
test "query `#{operation}` responds successfully" do
# Inside the test case, we run the query
{status, response} = AbsintheQueryAll.get_comprehensive_response(%{
schema: TestAbsintheSchema,
operation_type: :query,
operation_name: unquote(Macro.escape(operation)),
})
# And then we check to see that it returned successfully
assert status == :ok
assert Map.has_key?(response, :data)
refute Map.has_key?(response, :errors)
end
end
# Then we do the same thing with our mutations
for operation <- AbsintheQueryAll.get_all_operations(SomeAbsintheSchema, :mutation) do
test "mutation `#{operation}` responds successfully" do
{status, response} = AbsintheQueryAll.get_comprehensive_response(%{
schema: TestAbsintheSchema,
operation_type: :mutation,
operation_name: unquote(Macro.escape(operation)),
})
assert status == :ok
assert Map.has_key?(response, :data)
refute Map.has_key?(response, :errors)
end
end
end
```
With that setup, your app will automatically generate a test for every single graphql operation and make sure that a response is being generated for every single field in that operation. Just a nice, simple extra layer protection against a specific type of bug.
## Extra details
### Generating Arguments
Your schema likely has operations that require arguments. In order to generate arguments to pass to those operations, the `get_comprehensive_response` function has an optional argument, `argument_generator`, that you can pass an argument generating function to. The argument generating function should take two arguments: the first is the `operation_name` (e.g. `:someQuery`) and the second is the name of the argument you're generating (as an atom; e.g. `:userId`).
For example, you might call `get_comprehensive_response` like this:
```elixir
AbsintheQueryAll.get_comprehensive_response(%{
schema: SomeAbsintheSchema,
operation_type: :query,
operation_name: :testQuery,
argument_generator: fn operation_name, argument_name ->
case {operation_name, argument_name} do
{:someQuery, :theNameOfSomeStringArgumentToTheQuery} ->
# if this argument requires a string, here's where you choose what string it gets
"some string"
{:someQuery, :theNameOfSomeIntegerArgumentToTheQuery} ->
7
end
})
```
### Passing in Context
The optional `context` argument on `get_comprehensive_response` is just for passing along the `context` map that `Absinthe.run` takes. You might use this for something like passing in information on the currently logged in user if that has an effect on what your graphql endpoint will return for some operations.
```elixir
AbsintheQueryAll.get_comprehensive_response(%{
schema: SomeAbsintheSchema,
operation_type: :query,
operation_name: :testQuery,
context: %{current_user: user}
})
```
### Aliasing Options
The option to pass `parent_alias_generator` and `leaf_alias_generator` functions will most likely not be useful to most people, unless you, (A) specifically need to test something involving aliases, or (B) are using the utility functions available in this package to interface with tools like [elm-graphql](https://github.com/dillonkearns/elm-graphql) that automatically alias fields. But they are there if you need them.
```elixir
iex> Query.comprehensive(%{
...> schema: SomeAbsintheSchema,
...> operation_type: :query,
...> operation_name: :testQuery,
...> parent_alias_generator: fn _field_name, _field_args -> "someAlias" end
...> })
"query testQuery {\n someAlias: testQuery { someField }\n}\n"
```
```elixir
iex> Query.comprehensive(%{
...> schema: SomeAbsintheSchema,
...> operation_type: :query,
...> operation_name: :testQuery,
...> leaf_alias_generator: fn field_name, _field_details -> "#{field_name}Alias" end
...> })
"query testQuery {\n testQuery { someFieldAlias: someField }\n}\n"
```
## Other possibilities
This library intentionally exposes a lot of the underlying modules/functions that make the two main functions work, in case anyone wants to use them to build other tools. I use them for a variety of nice graphql testing functions that I may take a stab at generalizing and open-sourcing in the future if I find the time.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `absinthe_query_all` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:absinthe_query_all, "~> 0.1.1"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/absinthe_query_all>.