diff options
Diffstat (limited to 'doc/development/api_graphql_styleguide.md')
-rw-r--r-- | doc/development/api_graphql_styleguide.md | 100 |
1 files changed, 83 insertions, 17 deletions
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index bf2d6400f56..18fc0fb7d33 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -14,7 +14,7 @@ which is exposed as an API endpoint at `/api/graphql`. In March 2019, Nick Thomas hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`) on GitLab's [GraphQL API](../api/graphql/index.md) to share his domain specific knowledge -with anyone who may work in this part of the code base in the future. You can find the +with anyone who may work in this part of the codebase in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=-9L_1MWrjkg), and the slides on [Google Slides](https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit) and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/8e78ea7f326b2ef649e7d7d569c26d56/GraphQL_Deep_Dive__Create_.pdf). @@ -33,7 +33,7 @@ Authentication happens through the `GraphqlController`, right now this uses the same authentication as the Rails application. So the session can be shared. -It is also possible to add a `private_token` to the querystring, or +It's also possible to add a `private_token` to the query string, or add a `HTTP_PRIVATE_TOKEN` header. ## Global IDs @@ -41,7 +41,7 @@ add a `HTTP_PRIVATE_TOKEN` header. GitLab's GraphQL API uses Global IDs (i.e: `"gid://gitlab/MyObject/123"`) and never database primary key IDs. -Global ID is [a standard](https://graphql.org/learn/global-object-identification/) +Global ID is [a convention](https://graphql.org/learn/global-object-identification/) used for caching and fetching in client-side libraries. See also: @@ -75,7 +75,7 @@ The `iid`, `title` and `description` are _scalar_ GraphQL types. When exposing a model through the GraphQL API, we do so by creating a new type in `app/graphql/types`. You can also declare custom GraphQL data types -for scalar data types (e.g. `TimeType`). +for scalar data types (for example `TimeType`). When exposing properties in a type, make sure to keep the logic inside the definition as minimal as possible. Instead, consider moving any @@ -142,7 +142,10 @@ def reply_id end ``` -### Connection Types +### Connection types + +TIP: **Tip:** +For specifics on implementation, see [Pagination implementation](#pagination-implementation). GraphQL uses [cursor based pagination](https://graphql.org/learn/pagination/#pagination-and-edges) @@ -363,16 +366,16 @@ def foo end ``` -## Deprecating fields +## Deprecating fields and enum values GitLab's GraphQL API is versionless, which means we maintain backwards compatibility with older versions of the API with every change. Rather -than removing a field, we need to _deprecate_ the field instead. In -future, GitLab -[may remove deprecated fields](https://gitlab.com/gitlab-org/gitlab/-/issues/32292). +than removing a field or [enum value](#enums), we need to _deprecate_ it instead. +In future, GitLab +[may remove deprecated parts of the schema](https://gitlab.com/gitlab-org/gitlab/-/issues/32292). -Fields are deprecated using the `deprecated` property. The value -of the property is a `Hash` of: +Fields and enum values are deprecated using the `deprecated` property. +The value of the property is a `Hash` of: - `reason` - Reason for the deprecation. - `milestone` - Milestone that the field was deprecated. @@ -385,13 +388,14 @@ field :token, GraphQL::STRING_TYPE, null: true, description: 'Token for login' ``` -The original `description:` of the field should be maintained, and should -_not_ be updated to mention the deprecation. +The original `description` of the things being deprecated should be maintained, +and should _not_ be updated to mention the deprecation. Instead, the `reason` will +be appended to the `description`. ### Deprecation reason style guide -Where the reason for deprecation is due to the field being replaced -with another field, the `reason` must be: +Where the reason for deprecation is due to the field or enum value being +replaced, the `reason` must be: ```plaintext Use `otherFieldName` @@ -405,9 +409,22 @@ field :designs, ::Types::DesignManagement::DesignCollectionType, null: true, description: 'The designs associated with this issue', ``` +```ruby +module Types + class TodoStateEnum < BaseEnum + value 'pending', deprecated: { reason: 'Use PENDING', milestone: '10.0' } + value 'done', deprecated: { reason: 'Use DONE', milestone: '10.0' } + value 'PENDING', value: 'pending' + value 'DONE', value: 'done' + end +end +``` + If the field is not being replaced by another field, a descriptive deprecation `reason` should be given. +See also [Aliasing and deprecating mutations](#aliasing-and-deprecating-mutations). + ## Enums GitLab GraphQL enums are defined in `app/graphql/types`. When defining new enums, the @@ -452,6 +469,9 @@ module Types end ``` +Enum values can be deprecated using the +[`deprecated` keyword](#deprecating-fields-and-enum-values). + ## JSON When data to be returned by GraphQL is stored as @@ -760,6 +780,44 @@ to advertise the need for lookahead: For an example of real world use, please see [`ResolvesMergeRequests`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/graphql/resolvers/concerns/resolves_merge_requests.rb). +## Pass a parent object into a child Presenter + +Sometimes you need to access the resolved query parent in a child context to compute fields. Usually the parent is only +available in the `Resolver` class as `parent`. + +To find the parent object in your `Presenter` class: + +1. Add the parent object to the GraphQL `context` from within your resolver's `resolve` method: + + ```ruby + def resolve(**args) + context[:parent_object] = parent + end + ``` + +1. Declare that your fields require the `parent` field context. For example: + + ```ruby + # in ChildType + field :computed_field, SomeType, null: true, + method: :my_computing_method, + extras: [:parent], # Necessary + description: 'My field description' + ``` + +1. Declare your field's method in your Presenter class and have it accept the `parent` keyword argument. +This argument contains the parent **GraphQL context**, so you have to access the parent object with +`parent[:parent_object]` or whatever key you used in your `Resolver`: + + ```ruby + # in ChildPresenter + def my_computing_method(parent:) + # do something with `parent[:parent_object]` here + end + ``` + +For an example of real-world use, check [this MR that added `scopedPath` and `scopedUrl` to `IterationPresenter`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39543) + ## Mutations Mutations are used to change any stored values, or to trigger @@ -1114,7 +1172,8 @@ mount_aliased_mutation 'BarMutation', Mutations::FooMutation ``` This allows us to rename a mutation and continue to support the old name, -when coupled with the [`deprecated`](#deprecating-fields) argument. +when coupled with the [`deprecated`](#deprecating-fields-and-enum-values) +argument. Example: @@ -1130,6 +1189,10 @@ tested for within the unit test of `Types::MutationType`. The merge request can be referred to as an example of this, including the method of testing deprecated aliased mutations. +## Pagination implementation + +To learn more, visit [GraphQL pagination](graphql_guide/pagination.md). + ## Validating arguments For validations of single arguments, use the @@ -1199,6 +1262,9 @@ 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. +If you're adding a field to a query that supports pagination and sorting, +visit [Testing](graphql_guide/pagination.md#testing) for details. + To test GraphQL mutation requests, `GraphqlHelpers` provides 2 helpers: `graphql_mutation` which takes the name of the mutation, and a hash with the input for the mutation. This will return a struct with @@ -1285,7 +1351,7 @@ end More about complexity: [GraphQL Ruby documentation](https://graphql-ruby.org/queries/complexity_and_depth.html). -## Documentation and Schema +## Documentation and schema Our schema is located at `app/graphql/gitlab_schema.rb`. See the [schema reference](../api/graphql/reference/index.md) for details. |