2018-05-21 07:52:24 +00:00
|
|
|
# GraphQL API
|
|
|
|
|
|
|
|
## Authentication
|
|
|
|
|
2018-05-23 07:55:14 +00:00
|
|
|
Authentication happens through the `GraphqlController`, right now this
|
|
|
|
uses the same authentication as the Rails application. So the session
|
2018-05-21 07:52:24 +00:00
|
|
|
can be shared.
|
|
|
|
|
|
|
|
It is also possible to add a `private_token` to the querystring, or
|
|
|
|
add a `HTTP_PRIVATE_TOKEN` header.
|
|
|
|
|
|
|
|
### Authorization
|
|
|
|
|
2018-05-23 07:55:14 +00:00
|
|
|
Fields can be authorized using the same abilities used in the Rails
|
2018-05-21 07:52:24 +00:00
|
|
|
app. This can be done using the `authorize` helper:
|
|
|
|
|
|
|
|
```ruby
|
2018-05-23 07:55:14 +00:00
|
|
|
module Types
|
|
|
|
class QueryType < BaseObject
|
|
|
|
graphql_name 'Query'
|
2018-05-21 07:52:24 +00:00
|
|
|
|
2018-05-23 07:55:14 +00:00
|
|
|
field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
|
|
|
|
authorize :read_project
|
2018-05-21 07:52:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
The object found by the resolve call is used for authorization.
|
|
|
|
|
2018-05-23 07:55:14 +00:00
|
|
|
This works for authorizing a single record, for authorizing
|
|
|
|
collections, we should only load what the currently authenticated user
|
|
|
|
is allowed to view. Preferably we use our existing finders for that.
|
2018-05-21 07:52:24 +00:00
|
|
|
|
|
|
|
## Types
|
|
|
|
|
|
|
|
When exposing a model through the GraphQL API, we do so by creating a
|
|
|
|
new type in `app/graphql/types`.
|
|
|
|
|
|
|
|
When exposing properties in a type, make sure to keep the logic inside
|
|
|
|
the definition as minimal as possible. Instead, consider moving any
|
|
|
|
logic into a presenter:
|
|
|
|
|
|
|
|
```ruby
|
2018-05-23 07:55:14 +00:00
|
|
|
class Types::MergeRequestType < BaseObject
|
2018-05-21 07:52:24 +00:00
|
|
|
present_using MergeRequestPresenter
|
|
|
|
|
|
|
|
name 'MergeRequest'
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
An existing presenter could be used, but it is also possible to create
|
|
|
|
a new presenter specifically for GraphQL.
|
|
|
|
|
|
|
|
The presenter is initialized using the object resolved by a field, and
|
|
|
|
the context.
|
|
|
|
|
2018-05-23 07:55:14 +00:00
|
|
|
## Resolvers
|
|
|
|
|
|
|
|
To find objects to display in a field, we can add resolvers to
|
|
|
|
`app/graphql/resolvers`.
|
|
|
|
|
|
|
|
Arguments can be defined within the resolver, those arguments will be
|
|
|
|
made available to the fields using the resolver.
|
|
|
|
|
|
|
|
We already have a `FullPathLoader` that can be included in other
|
|
|
|
resolvers to quickly find Projects and Namespaces which will have a
|
|
|
|
lot of dependant objects.
|
|
|
|
|
|
|
|
To limit the amount of queries performed, we can use `BatchLoader`.
|
|
|
|
|
2018-05-21 07:52:24 +00:00
|
|
|
## Testing
|
|
|
|
|
|
|
|
_full stack_ tests for a graphql query or mutation live in
|
2018-05-23 07:55:14 +00:00
|
|
|
`spec/requests/api/graphql`.
|
2018-05-21 07:52:24 +00:00
|
|
|
|
|
|
|
When adding a query, the `a working graphql query` shared example can
|
2018-05-23 07:55:14 +00:00
|
|
|
be used to test if the query renders valid results.
|
|
|
|
|
|
|
|
Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query
|
|
|
|
including all available fields can be constructed. This makes it easy
|
|
|
|
to add a test rendering all possible fields for a query.
|