diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-29 06:06:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-29 06:06:31 +0000 |
commit | 2ac93cb80c4c0a57fde86de8262b569d1e9b9e51 (patch) | |
tree | 3f74cb04801cb4dcea27c8e1b4d24b783b4f1ec3 /doc | |
parent | 23d8718bf3a114f7b832a9c493b1efcdc6decedb (diff) | |
download | gitlab-ce-2ac93cb80c4c0a57fde86de8262b569d1e9b9e51.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'doc')
-rw-r--r-- | doc/development/api_graphql_styleguide.md | 96 | ||||
-rw-r--r-- | doc/development/fe_guide/graphql.md | 44 | ||||
-rw-r--r-- | doc/development/testing_guide/end_to_end/quick_start_guide.md | 12 |
3 files changed, 149 insertions, 3 deletions
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index 1cf2ca3667d..1ef0b928820 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -1,5 +1,13 @@ # GraphQL API +## How GitLab implements GraphQL + +We use the [graphql-ruby gem](https://graphql-ruby.org/) written by [Robert Mosolgo](https://github.com/rmosolgo/). + +All GraphQL queries are directed to a single endpoint +([`app/controllers/graphql_controller.rb#execute`](https://gitlab.com/gitlab-org/gitlab/blob/master/app%2Fcontrollers%2Fgraphql_controller.rb)), +which is exposed as an API endpoint at `/api/graphql`. + ## Deep Dive In March 2019, Nick Thomas hosted a [Deep Dive](https://gitlab.com/gitlab-org/create-stage/issues/1) @@ -22,8 +30,31 @@ add a `HTTP_PRIVATE_TOKEN` header. ## Types +We use a code-first schema, and we declare what type everything is in Ruby. + +For example, `app/graphql/types/issue_type.rb`: + +```ruby +graphql_name 'Issue' + +field :iid, GraphQL::ID_TYPE, null: false +field :title, GraphQL::STRING_TYPE, null: false + +# we also have a method here that we've defined, that extends `field` +markdown_field :title_html, null: true +field :description, GraphQL::STRING_TYPE, null: true +markdown_field :description_html, null: true +``` + +We give each type a name (in this case `Issue`). + +The `iid`, `title` and `description` are _scalar_ GraphQL types. +`iid` is a `GraphQL::ID_TYPE`, a special string type that signifies a unique ID. +`title` and `description` are regular `GraphQL::STRING_TYPE` types. + When exposing a model through the GraphQL API, we do so by creating a -new type in `app/graphql/types`. +new type in `app/graphql/types`. You can also declare custom GraphQL data types +for scalar data types (e.g. `TimeType`). When exposing properties in a type, make sure to keep the logic inside the definition as minimal as possible. Instead, consider moving any @@ -293,6 +324,8 @@ If the: - Resource is part of a collection, the collection will be filtered to exclude the objects that the user's authorization checks failed against. +Also see [authorizing resources in a mutation](#authorizing-resources). + TIP: **Tip:** Try to load only what the currently authenticated user is allowed to view with our existing finders first, without relying on authorization @@ -391,6 +424,11 @@ end ## Resolvers +We define how the application serves the response using _resolvers_ +stored in the `app/graphql/resolvers` directory. +The resolver provides the actual implementation logic for retrieving +the objects in question. + To find objects to display in a field, we can add resolvers to `app/graphql/resolvers`. @@ -618,7 +656,61 @@ it 'returns a successful response' do end ``` +## Notes about Query flow and GraphQL infrastructure + +GitLab's GraphQL infrastructure can be found in `lib/gitlab/graphql`. + +[Instrumentation](https://graphql-ruby.org/queries/instrumentation.html) is functionality +that wraps around a query being executed. It is implemented as a module that uses the `Instrumentation` class. + +Example: `Present` + +```ruby +module Present + #... some code above... + + def self.use(schema_definition) + schema_definition.instrument(:field, Instrumentation.new) + end +end +``` + +A [Query Analyzer](https://graphql-ruby.org/queries/analysis.html#analyzer-api) contains a series +of callbacks to validate queries before they are executed. Each field can pass through +the analyzer, and the final value is also available to you. + +[Multiplex queries](https://graphql-ruby.org/queries/multiplex.html) enable +multiple queries to be sent in a single request. This reduces the number of requests sent to the server. +(there are custom Multiplex Query Analyzers and Multiplex Instrumentation provided by graphql-ruby). + +### Query limits + +Queries and mutations are limited by depth, complexity, and recursion +to protect server resources from overly ambitious or malicious queries. +These values can be set as defaults and overridden in specific queries as needed. +The complexity values can be set per object as well, and the final query complexity is +evaluated based on how many objects are being returned. This is useful +for objects that are expensive (e.g. requiring Gitaly calls). + +For example, a conditional complexity method in a resolver: + +```ruby +def self.resolver_complexity(args, child_complexity:) + complexity = super + complexity += 2 if args[:labelName] + + complexity +end +``` + +More about complexity: +[graphql-ruby docs](https://graphql-ruby.org/queries/complexity_and_depth.html) + ## Documentation and Schema +Our schema is located at `app/graphql/gitlab_schema.rb`. +See the [schema reference](../api/graphql/reference/index.md) for details. + +This generated GraphQL documentation needs to be updated when the schema changes. For information on generating GraphQL documentation and schema files, see -[Rake tasks related to GraphQL](rake_tasks.md#update-graphql-documentation-and-schema-definitions). +[updating the schema documentation](rake_tasks.md#update-graphql-documentation-and-schema-definitions). diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md index 894a613ec2d..b813ea24750 100644 --- a/doc/development/fe_guide/graphql.md +++ b/doc/development/fe_guide/graphql.md @@ -39,6 +39,50 @@ To distinguish queries from mutations and fragments, the following naming conven - `addUser.mutation.graphql` for mutations; - `basicUser.fragment.graphql` for fragments. +GraphQL: + +- Queries are stored in `(ee/)app/assets/javascripts/` under the feature. For example, `respository/queries`. Frontend components can use these stored queries. +- Mutations are stored in + `(ee/)app/assets/javascripts/<subfolders>/<name of mutation>.mutation.graphql`. + +### Fragments + +Fragments are a way to make your complex GraphQL queries more readable and re-usable. +They can be stored in a separate file and imported. + +For example, a fragment that references another fragment: + +```ruby +fragment BaseEpic on Epic { + id + iid + title + webPath + relativePosition + userPermissions { + adminEpic + createEpic + } +} + +fragment EpicNode on Epic { + ...BaseEpic + state + reference(full: true) + relationPath + createdAt + closedAt + hasChildren + hasIssues + group { + fullPath + } +} +``` + +More about fragments: +[GraphQL Docs](https://graphql.org/learn/queries/#fragments) + ## Usage in Vue To use Vue Apollo, import the [Vue Apollo][vue-apollo] plugin as well diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md index 2457d8ada5a..16a45bc5ef0 100644 --- a/doc/development/testing_guide/end_to_end/quick_start_guide.md +++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md @@ -26,7 +26,17 @@ If you don't exactly understand what we mean by **not everything needs to happen At GitLab we respect the [test pyramid](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md), and so, we recommend you check the code coverage of a specific feature before writing end-to-end tests, for both [CE](https://gitlab-org.gitlab.io/gitlab-foss/coverage-ruby/#_AllFiles) and [EE](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/#_AllFiles) projects. -Sometimes you may notice that there is already good coverage in other test levels, and we can stay confident that if we break a feature, we will still have quick feedback about it, even without having end-to-end tests. +Sometimes you may notice that there is already good coverage in lower test levels, and we can stay confident that if we break a feature, we will still have quick feedback about it, even without having end-to-end tests. + +> For analyzing the code coverage, you will also need to understand which application files implement specific functionalities. + +#### Some other guidelines are as follows + +- Take a look at the [How to test at the correct level?](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md#how-to-test-at-the-correct-level) section of the [Testing levels](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/development/testing_guide/testing_levels.md) document + +- Look into the frequency in which such a feature is changed (_Stable features that don't change very often might not be worth covering with end-to-end tests if they're already covered in lower levels_) + +- Finally, discuss with the developer(s) involved in developing the feature and the tests themselves, to get their feeling If after this analysis you still think that end-to-end tests are needed, keep reading. |