# GraphQL API style guide
> 原文:[https://docs.gitlab.com/ee/development/api_graphql_styleguide.html](https://docs.gitlab.com/ee/development/api_graphql_styleguide.html)
* [How GitLab implements GraphQL](#how-gitlab-implements-graphql)
* [Deep Dive](#deep-dive)
* [GraphiQL](#graphiql)
* [Authentication](#authentication)
* [Types](#types)
* [Nullable fields](#nullable-fields)
* [Exposing Global IDs](#exposing-global-ids)
* [Connection Types](#connection-types)
* [Shortcut fields](#shortcut-fields)
* [Exposing permissions for a type](#exposing-permissions-for-a-type)
* [Feature flags](#feature-flags)
* [`feature_flag` property](#feature_flag-property)
* [Toggle the value of a field](#toggle-the-value-of-a-field)
* [Deprecating fields](#deprecating-fields)
* [Deprecation reason style guide](#deprecation-reason-style-guide)
* [Enums](#enums)
* [Descriptions](#descriptions)
* [Description style guide](#description-style-guide)
* [`copy_field_description` helper](#copy_field_description-helper)
* [Authorization](#authorization)
* [Type authorization](#type-authorization)
* [Field authorization](#field-authorization)
* [Type and Field authorizations together](#type-and-field-authorizations-together)
* [Resolvers](#resolvers)
* [Correct use of `Resolver#ready?`](#correct-use-of-resolverready)
* [Look-Ahead](#look-ahead)
* [Mutations](#mutations)
* [Building Mutations](#building-mutations)
* [Naming conventions](#naming-conventions)
* [Arguments](#arguments)
* [Fields](#fields)
* [The `resolve` method](#the-resolve-method)
* [Mounting the mutation](#mounting-the-mutation)
* [Authorizing resources](#authorizing-resources)
* [Errors in mutations](#errors-in-mutations)
* [Success](#success)
* [Failure (relevant to the user)](#failure-relevant-to-the-user)
* [Failure (irrelevant to the user)](#failure-irrelevant-to-the-user)
* [Categorizing errors](#categorizing-errors)
* [Aliasing and deprecating mutations](#aliasing-and-deprecating-mutations)
* [Validating arguments](#validating-arguments)
* [GitLab’s custom scalars](#gitlabs-custom-scalars)
* [`Types::TimeType`](#typestimetype)
* [Testing](#testing)
* [Notes about Query flow and GraphQL infrastructure](#notes-about-query-flow-and-graphql-infrastructure)
* [Query limits](#query-limits)
* [Documentation and Schema](#documentation-and-schema)
# GraphQL API style guide[](#graphql-api-style-guide "Permalink")
本文档概述了 GitLab 的[GraphQL API](../api/graphql/index.html)的样式指南.
## How GitLab implements GraphQL[](#how-gitlab-implements-graphql "Permalink")
我们使用[Robert Mosolgo](https://github.com/rmosolgo/)编写的[GraphQL Ruby 宝石](https://graphql-ruby.org/) .
所有 GraphQL 查询都定向到单个端点( [`app/controllers/graphql_controller.rb#execute`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/controllers/graphql_controller.rb) ),该端点在`/api/graphql`处作为 API 端点`/api/graphql` .
## Deep Dive[](#deep-dive "Permalink")
在 2019 年 3 月,尼克·托马斯(Nick Thomas)在 GitLab 的[GraphQL API 上](../api/graphql/index.html)进行了一次深潜(仅限 GitLab 团队成员: `https://gitlab.com/gitlab-org/create-stage/issues/1` : [//gitlab.com/gitlab-org/create-stage/issues/1](../api/graphql/index.html) ),以与可能将来会在代码库的这一部分中工作. 您可以[在 YouTube 上](https://www.youtube.com/watch?v=-9L_1MWrjkg)找到[录音](https://www.youtube.com/watch?v=-9L_1MWrjkg) ,在[Google 幻灯片](https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit)和[PDF 中](https://gitlab.com/gitlab-org/create-stage/uploads/8e78ea7f326b2ef649e7d7d569c26d56/GraphQL_Deep_Dive__Create_.pdf)找到[幻灯片](https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit) . 自 GitLab 11.9 起,本次深入学习中涉及的所有内容都是准确的,尽管自那时以来特定细节可能有所更改,但它仍应作为一个很好的介绍.
## GraphiQL[](#graphiql "Permalink")
GraphiQL 是一个交互式 GraphQL API 资源管理器,您可以在其中使用现有查询. 您可以在`https://<your-gitlab-site.com>/-/graphql-explorer`任何 GitLab 环境中访问它. 例如,用于[GitLab.com 的那个](https://gitlab.com/-/graphql-explorer) .
## Authentication[](#authentication "Permalink")
认证通过`GraphqlController` ,现在,它使用与 Rails 应用程序相同的认证. 因此可以共享会话.
也可以将`private_token`添加到查询字符串,或添加`HTTP_PRIVATE_TOKEN`标头.
## Types[](#types "Permalink")
我们使用代码优先模式,并声明 Ruby 中所有内容的类型.
例如, `app/graphql/types/issue_type.rb` :
```
graphql_name 'Issue'
field :iid, GraphQL::ID_TYPE, null: true
field :title, GraphQL::STRING_TYPE, null: true
# 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`).
`iid` , `title`和`description`是*标量* GraphQL 类型. `iid`是`GraphQL::ID_TYPE` ,这是一种特殊的字符串类型,表示唯一的 ID. `title`和`description`是常规的`GraphQL::STRING_TYPE`类型.
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`).
公开类型中的属性时,请确保将定义内的逻辑保持尽可能小. 相反,请考虑将任何逻辑转移到演示者中:
```
class Types::MergeRequestType < BaseObject
present_using MergeRequestPresenter
name 'MergeRequest'
end
```
可以使用现有的演示者,但是也可以专门为 GraphQL 创建一个新的演示者.
使用由字段解析的对象和上下文初始化演示者.
### Nullable fields[](#nullable-fields "Permalink")
GraphQL 允许字段为"可为空"或"不可为空". 前者意味着可以返回`null`而不是指定类型的值. **通常** ,出于以下原因,您应该首选使用可空字段而不是不可空字段:
* 数据从必需切换到不需要,然后再次返回是很常见的
* 即使没有可能成为可选字段的前景,在查询时它也可能不可**用**
* 例如,可能需要从 Gitaly 查找 blob 的`content`
* 如果`content`可为空,我们可以返回**部分**响应,而不是使整个查询失败
* 对于无版本模式,很难从不可为空的字段更改为可为空的字段
非空字段仅应在需要字段时使用,将来不太可能成为可选字段,并且非常容易计算. 一个示例是`id`字段.
进一步阅读:
* [GraphQL Best Practices Guide](https://s0graphql0org.icopy.site/learn/best-practices/)
* [Using nullability in GraphQL](https://www.apollographql.com/blog/using-nullability-in-graphql-2254f84c4ed7)
### Exposing Global IDs[](#exposing-global-ids "Permalink")
在类型上公开`ID`字段时,默认情况下,我们将通过在要渲染的资源上调用`to_global_id`来公开全局 ID.
要覆盖此行为,可以在要公开其 ID 的类型上实现`id`方法. 请确保在使用自定义方法公开`GraphQL::ID_TYPE` ,它是全局唯一的.
被曝光的记录`full_path`作为`ID_TYPE`是这些例外之一. 由于完整路径是`Project`或`Namespace`的唯一标识符.
### Connection Types[](#connection-types "Permalink")
GraphQL 使用[基于光标的分页](https://s0graphql0org.icopy.site/learn/pagination/)来公开项目的集合. 这为客户提供了很大的灵活性,同时还允许后端使用不同的分页模型.
为了公开资源的集合,我们可以使用连接类型. 这将使用默认的分页字段包装数组. 例如,对项目管道的查询可能如下所示:
```
query($project_path: ID!) { project(fullPath: $project_path) { pipelines(first: 2) { pageInfo { hasNextPage hasPreviousPage } edges { cursor node { id status } } } } }
```
这将返回项目的前两个管道和相关的分页信息,按降序 ID 排序. 返回的数据如下所示:
```
{ "data": { "project": { "pipelines": { "pageInfo": { "hasNextPage": true, "hasPreviousPage": false }, "edges": [ { "cursor": "Nzc=", "node": { "id": "gid://gitlab/Pipeline/77", "status": "FAILED" } }, { "cursor": "Njc=", "node": { "id": "gid://gitlab/Pipeline/67", "status": "FAILED" } } ] } } } }
```
要获取下一页,可以传递最后一个已知元素的光标:
```
query($project_path: ID!) { project(fullPath: $project_path) { pipelines(first: 2, after: "Njc=") { pageInfo { hasNextPage hasPreviousPage } edges { cursor node { id status } } } } }
```
为了确保获得一致的顺序,我们将在主键上按降序附加顺序. 这通常是`id` ,因此基本上我们将在关系的末尾添加`order(id: :desc)` . 基础表上*必须*有主键.
#### Shortcut fields[](#shortcut-fields "Permalink")
有时似乎很容易实现"快捷字段",如果没有传递任何参数,则让解析程序返回集合的第一个. 不鼓励使用这些"快捷字段",因为它们会增加维护开销. 它们需要与规范字段保持同步,并且如果规范字段发生更改,则不建议使用或修改它们. 除非有充分的理由,否则请使用框架提供的功能.
例如,不要使用`latest_pipeline` ,而应使用`pipelines(last: 1)` .
### Exposing permissions for a type[](#exposing-permissions-for-a-type "Permalink")
若要公开当前用户对资源的权限,可以调用以单独的类型传递的`expose_permissions` ,该类型表示资源的权限.
例如:
```
module Types
class MergeRequestType < BaseObject
expose_permissions Types::MergeRequestPermissionsType
end
end
```
权限类型继承自`BasePermissionType` ,其中包括一些帮助程序方法,这些方法允许将权限公开为不可为 null 的布尔值:
```
class MergeRequestPermissionsType < BasePermissionType
present_using MergeRequestPresenter
graphql_name 'MergeRequestPermissions'
abilities :admin_merge_request, :update_merge_request, :create_note
ability_field :resolve_note,
description: 'Indicates the user can resolve discussions on the merge request'
permission_field :push_to_source_branch, method: :can_push_to_source_branch?
end
```
* **`permission_field`** :其作用与`graphql-ruby`的`field`方法相同,但设置默认描述和类型,并使它们不可为空. 通过将它们添加为参数,仍然可以覆盖这些选项.
* **`ability_field`** :公开我们政策中定义的能力. 此行为与`permission_field`相同,并且可以覆盖相同的参数.
* **`abilities`** :允许一次暴露我们政策中定义的几种能力. 这些字段都将是带有默认说明的非空布尔值.
## Feature flags[](#feature-flags "Permalink")
开发人员可以通过以下方式将[功能标志](../development/feature_flags/index.html)添加到 GraphQL 字段:
* 将`feature_flag`属性添加到字段. 当禁用该标志时,这将允许从 GraphQL 模式中*隐藏*该字段.
* 解析字段时切换返回值.
您可以参考以下准则来决定使用哪种方法:
* 如果您的字段是实验性的,并且其名称或类型可能会发生变化,请使用`feature_flag`属性.
* 如果您的字段是稳定的,并且即使删除了标志,其定义也不会更改,请改为切换字段的返回值. 请注意, [所有字段](#nullable-fields)无论如何[都应该为空](#nullable-fields) .
### `feature_flag` property[](#feature_flag-property "Permalink")
`feature_flag`属性允许您在 GraphQL 模式中切换字段的[可见性](https://graphql-ruby.org/authorization/visibility.html) . 禁用该标志后,将从架构中删除该字段.
字段后[附有](https://gitlab.com/gitlab-org/gitlab/-/blob/497b556/app/graphql/types/base_field.rb#L44-53)说明,表示该说明位于功能标志的后面.
**警告:**如果在禁用功能标志时客户端查询该字段,则查询将失败. 在生产中打开或关闭功能的可见性时,请考虑此问题.
`feature_flag`属性不允许使用[基于 actor](../development/feature_flags/development.html)的[特征门](../development/feature_flags/development.html) . 这意味着功能标记不能仅针对特定的项目,组或用户进行切换,而只能针对所有人进行全局切换.
Example:
```
field :test_field, type: GraphQL::STRING_TYPE,
null: true,
description: 'Some test field',
feature_flag: :my_feature_flag
```
### Toggle the value of a field[](#toggle-the-value-of-a-field "Permalink")
对字段使用特征标记的这种方法是切换字段的返回值. 这可以在解析器中,在类型中甚至在模型方法中完成,具体取决于您的偏好和情况.
当应用功能标记来切换字段的值时,该字段的`description`必须:
* 说明该字段的值可以通过功能标记切换.
* 命名功能标志.
* 说明禁用(或启用,如果更合适的话)功能标志时字段将返回的内容.
Example:
```
field :foo, GraphQL::STRING_TYPE,
null: true,
description: 'Some test field. Will always return `null`' \
'if `my_feature_flag` feature flag is disabled'
def foo
object.foo unless Feature.enabled?(:my_feature_flag, object)
end
```
## Deprecating fields[](#deprecating-fields "Permalink")
GitLab 的 GraphQL API 是无版本的,这意味着我们会与 API 的旧版本保持向下兼容性. 除了删除字段,我们还需要*弃用*该字段. 将来,GitLab [可能会删除不推荐使用的字段](https://gitlab.com/gitlab-org/gitlab/-/issues/32292) .
使用`deprecated`推荐使用的属性不推荐使用字段. 该属性的值是以下各项的`Hash`值:
* `reason` -弃用的原因.
* `milestone` -已弃用该字段的里程碑.
Example:
```
field :token, GraphQL::STRING_TYPE, null: true,
deprecated: { reason: 'Login via token has been removed', milestone: '10.0' },
description: 'Token for login'
```
最初的`description:`现场应保持,并且*不*应该更新提折旧.
### Deprecation reason style guide[](#deprecation-reason-style-guide "Permalink")
如果弃用的原因是该字段被另一个字段替换,则`reason`必须是:
```
Use `otherFieldName`
```
Example:
```
field :designs, ::Types::DesignManagement::DesignCollectionType, null: true,
deprecated: { reason: 'Use `designCollection`', milestone: '10.0' },
description: 'The designs associated with this issue',
```
如果该字段没有被另一个字段替换,则应给出描述性的弃用`reason` .
## Enums[](#enums "Permalink")
GitLab GraphQL enums are defined in `app/graphql/types`. When defining new enums, the following rules apply:
* 值必须为大写.
* 类名必须以字符串`Enum`结尾.
* `graphql_name`不得包含字符串`Enum` .
例如:
```
module Types
class TrafficLightStateEnum < BaseEnum
graphql_name 'TrafficLightState'
description 'State of a traffic light'
value 'RED', description: 'Drivers must stop'
value 'YELLOW', description: 'Drivers must stop when it is safe to'
value 'GREEN', description: 'Drivers can start or keep driving'
end
end
```
If the enum will be used for a class property in Ruby that is not an uppercase string, you can provide a `value:` option that will adapt the uppercase value.
在以下示例中:
* GraphQL inputs of `OPENED` will be converted to `'opened'`.
* Ruby 的`'opened'`值将在 GraphQL 响应中转换为`"OPENED"` .
```
module Types
class EpicStateEnum < BaseEnum
graphql_name 'EpicState'
description 'State of a GitLab epic'
value 'OPENED', value: 'opened', description: 'An open Epic'
value 'CLOSED', value: 'closed', description: 'An closed Epic'
end
end
```
## Descriptions[](#descriptions "Permalink")
所有字段和参数都[必须具有描述](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16438) .
使用`description:`关键字给出字段或自变量的`description:` . 例如:
```
field :id, GraphQL::ID_TYPE, description: 'ID of the resource'
```
用户可以通过以下方式查看字段和参数的描述:
* The [GraphiQL explorer](#graphiql).
* The [static GraphQL API reference](../api/graphql/#reference).
### Description style guide[](#description-style-guide "Permalink")
为确保一致性,每次添加或更新描述时都应遵循以下规定:
* 在描述中提及资源的名称. 示例: `'Labels of the issue'` (问题就是资源).
* 尽可能`"{x} of the {y}"` . 示例: `'Title of the issue'` . 不要下手描述`The` .
* `GraphQL::BOOLEAN_TYPE`字段的描述应回答以下问题:"此字段的作用是什么?". 示例: `'Indicates project has a Git repository'` .
* 描述类型为`Types::TimeType`的参数或字段时,请始终包含单词`"timestamp"` . 这使读者知道该属性的格式将是`Time` ,而不仅仅是`Date` .
* 没有`.` 在字符串末尾.
Example:
```
field :id, GraphQL::ID_TYPE, description: 'ID of the Issue'
field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential'
field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed'
```
### `copy_field_description` helper[](#copy_field_description-helper "Permalink")
有时我们希望确保两个描述始终相同. 例如,当两个类型字段描述都表示相同的属性时,它们要与突变参数保持相同.
除了提供描述之外,我们还可以使用`copy_field_description`帮助器,将其类型和字段名称传递给它,以复制其描述.
Example:
```
argument :title, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::MergeRequestType, :title)
```
## Authorization[](#authorization "Permalink")
可以使用与 Rails 应用程序中相同的功能将授权应用于类型和字段.
如果:
* 当前经过身份验证的用户未通过授权,授权资源将返回`null` .
* 资源是集合的一部分,该集合将被过滤以排除用户授权检查失败的对象.
另请参见[在变异中授权资源](#authorizing-resources) .
**提示:**尝试仅先加载允许当前已认证用户使用我们现有的查找器查看的内容,而不依赖于授权来过滤记录. 这样可以最大程度地减少数据库查询和对已加载记录的不必要的授权检查.
### Type authorization[](#type-authorization "Permalink")
通过将能力传递给`authorize`方法来`authorize`类型. 通过检查当前经过身份验证的用户是否具有所需的能力,将对所有具有相同类型的字段进行授权.
例如,以下授权可确保当前经过身份验证的用户只能看到其具有`read_project`能力的项目(只要在使用`Types::ProjectType`的字段中返回该`Types::ProjectType` ):
```
module Types
class ProjectType < BaseObject
authorize :read_project
end
end
```
您还可以授权多个能力,在这种情况下,所有能力检查都必须通过.
例如,以下授权可确保当前经过身份验证的用户必须具有`read_project`和`another_ability`能力才能查看项目:
```
module Types
class ProjectType < BaseObject
authorize [:read_project, :another_ability]
end
end
```
### Field authorization[](#field-authorization "Permalink")
可以使用`authorize`选项对字段进行授权.
例如,以下授权可确保当前经过身份验证的用户必须具有`owner_access`功能才能查看项目:
```
module Types
class MyType < BaseObject
field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver, authorize: :owner_access
end
end
```
还可以针对多个能力授权字段,在这种情况下,所有能力检查都必须通过. **注意:**这需要显式地将一个块传递给`field` :
```
module Types
class MyType < BaseObject
field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
authorize [:owner_access, :another_ability]
end
end
end
```
**注意:**如果字段的类型已经[具有特定的授权,](#type-authorization)则无需将该相同的授权添加到字段中.
### Type and Field authorizations together[](#type-and-field-authorizations-together "Permalink")
授权是累积性的,因此,在一个字段以及该字段的类型上定义了授权的情况下,当前经过身份验证的用户将需要通过所有能力检查.
在下面的简化示例中,当前经过身份验证的用户将需要`first_permission`和`second_permission`能力,才能看到问题的作者.
```
class UserType
authorize :first_permission
end
```
```
class IssueType
field :author, UserType, authorize: :second_permission
end
```
## Resolvers[](#resolvers "Permalink")
我们使用存储在`app/graphql/resolvers`目录中的*解析器*定义应用程序如何响应. 解析器提供了用于检索相关对象的实际实现逻辑.
要查找要显示在字段中的对象,我们可以将解析器添加到`app/graphql/resolvers` .
可以在解析程序中定义参数,这些参数将通过解析程序提供给字段. 公开具有内部 ID( `iid` )的模型时,最好将其与名称空间路径结合使用,作为解析器中的参数,而不是数据库 ID. 否则,请使用[全局唯一 ID](#exposing-global-ids) .
我们已经有一个`FullPathLoader` ,可以将其包含在其他解析器中,以快速查找将有很多依赖对象的项目和命名空间.
为了限制执行的查询数量,我们可以使用`BatchLoader` .
### Correct use of `Resolver#ready?`[](#correct-use-of-resolverready "Permalink")
解析器有两个公共 API 方法作为框架的一部分: `#ready?(**args)`和`#resolve(**args)` . 我们可以使用`#ready?` 无需调用`#resolve`即可执行设置,验证或提前退货.
有充分理由使用`#ready?` 包括:
* 验证互斥参数(请参阅[验证参数](#validating-arguments) )
* 如果事先知道没有结果,则返回`Relation.none`
* 执行诸如初始化实例变量的设置(尽管为此考虑了延迟初始化的方法)
[`Resolver#ready?(**args)`](https://graphql-ruby.org/api-doc/1.10.9/GraphQL/Schema/Resolver#ready?-instance_method)应返回`(Boolean, early_return_data)` ,如下所示:
```
def ready?(**args)
[false, 'have this instead']
end
```
因此,无论何时调用解析器(主要是在测试中-作为框架抽象,不应将解析器视为可重用的,最好使用查找器),还记得调用`ready?` 方法,并在调用`resolve`之前检查布尔值标志! 在我们的[`GraphQLHelpers`](https://gitlab.com/gitlab-org/gitlab/-/blob/2d395f32d2efbb713f7bc861f96147a2a67e92f2/spec/support/helpers/graphql_helpers.rb#L20-27)可以看到一个示例.
### Look-Ahead[](#look-ahead "Permalink")
完整查询是在执行期间预先知道的,这意味着我们可以利用[超前](https://graphql-ruby.org/queries/lookahead.html)查询来优化查询,并知道我们将需要的批处理负载关联. 考虑在解析器中添加前瞻性支持,以避免`N+1`性能问题.
为了支持常见的前瞻用例(在请求子字段时预加载关联),可以包含[`LooksAhead`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/graphql/resolvers/concerns/looks_ahead.rb) . 例如:
```
# Assuming a model `MyThing` with attributes `[child_attribute, other_attribute, nested]`,
# where nested has an attribute named `included_attribute`.
class MyThingResolver < BaseResolver
include LooksAhead
# Rather than defining `resolve(**args)`, we implement: `resolve_with_lookahead(**args)`
def resolve_with_lookahead(**args)
apply_lookahead(MyThingFinder.new(current_user).execute)
end
# We list things that should always be preloaded:
# For example, if child_attribute is always needed (during authorization
# perhaps), then we can include it here.
def unconditional_includes
[:child_attribute]
end
# We list things that should be included if a certain field is selected:
def preloads
{
field_one: [:other_attribute],
field_two: [{ nested: [:included_attribute] }]
}
end
end
```
需要做的最后一件事是,使用此解析器的每个字段都需要公告提前查询的需求:
```
# in ParentType
field :my_things, MyThingType.connection_type, null: true,
extras: [:lookahead], # Necessary
resolver: MyThingResolver,
description: 'My things'
```
有关实际使用的示例,请参见[`ResolvesMergeRequests`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/graphql/resolvers/concerns/resolves_merge_requests.rb) .
## Mutations[](#mutations "Permalink")
变异用于更改任何存储的值或触发动作. 与 GET 请求不应修改数据的方式相同,我们无法在常规 GraphQL 查询中修改数据. 但是我们可以突变.
要查找突变的对象,需要指定参数. 与[解析程序一样](#resolvers) ,最好使用内部 ID 或全局 ID(而不是数据库 ID)(如果需要).
### Building Mutations[](#building-mutations "Permalink")
突变存在于`app/graphql/mutations`理想情况下,突变是根据它们正在突变的资源进行分组的,类似于我们的服务. 他们应该继承`Mutations::BaseMutation` . 突变的结果将返回在突变上定义的字段.
### Naming conventions[](#naming-conventions "Permalink")
每个突变都必须定义一个`graphql_name` ,这是 GraphQL 模式中的突变名称.
Example:
```
class UserUpdateMutation < BaseMutation
graphql_name 'UserUpdate'
end
```
我们的 GraphQL 突变名称在历史上是不一致的,但是新的突变名称应遵循约定`'{Resource}{Action}'`或`'{Resource}{Action}{Attribute}'` .
**创建**新资源的变异应使用动词`Create` .
Example:
* `CommitCreate`
**更新**数据的突变应使用:
* 动词`Update` .
* 特定于域的动词,例如`Set` , `Add`或`Toggle`如果更合适).
Examples:
* `EpicTreeReorder`
* `IssueSetWeight`
* `IssueUpdate`
* `TodoMarkDone`
**删除**数据的突变应使用:
* 动词`Delete`而不是`Destroy` .
* 特定于域的动词,例如" `Remove`如果更合适).
Examples:
* `AwardEmojiRemove`
* `NoteDelete`
如果您需要有关突变命名的建议,请查看 Slack `#graphql`渠道以获取反馈.
### Arguments[](#arguments "Permalink")
突变所需的参数可以定义为字段所需的参数. 这些将被包装为突变的输入类型. 例如,带有 GraphQL 名称`MergeRequestSetWip`的`Mutations::MergeRequests::SetWip` `MergeRequestSetWip`定义了以下参数:
```
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the merge request to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
required: true,
description: "The iid of the merge request to mutate"
argument :wip,
GraphQL::BOOLEAN_TYPE,
required: false,
description: <<~DESC Whether or not to set the merge request as a WIP.
If not passed, the value will be toggled. DESC
```
这将自动生成一个名为`MergeRequestSetWipInput`的输入类型, `MergeRequestSetWipInput`包含我们指定的 3 个参数和`clientMutationId` .
然后将这些参数作为关键字参数传递给突变的`resolve`方法.
### Fields[](#fields "Permalink")
在最常见的情况下,变异会返回 2 个字段:
* 正在修改的资源
* 错误列表,说明无法执行该操作的原因. 如果突变成功,此列表将为空.
通过从`Mutations::BaseMutation`继承任何新的突变, `errors`字段将自动添加. 还添加了一个`clientMutationId`字段,当在单个请求中执行多个变异时,客户端可以使用它来标识单个变异的结果.
### The `resolve` method[](#the-resolve-method "Permalink")
`resolve`方法接收变异的参数作为关键字参数. 从这里,我们可以调用将修改资源的服务.
然后, `resolve`方法应返回一个哈希,该哈希具有与在突变上定义的字段名称相同的字段名称,包括`errors`数组. 例如, `Mutations::MergeRequests::SetWip`定义了`merge_request`字段:
```
field :merge_request,
Types::MergeRequestType,
null: true,
description: "The merge request after mutation"
```
这意味着在此突变中从`resolve`返回的哈希应如下所示:
```
{
# The merge request modified, this will be wrapped in the type
# defined on the field
merge_request: merge_request,
# An array of strings if the mutation failed after authorization.
# The `errors_on_object` helper collects `errors.full_messages`
errors: errors_on_object(merge_request)
}
```
### Mounting the mutation[](#mounting-the-mutation "Permalink")
为了使变异可用,必须在存在于`graphql/types/mutation_types`的变异类型上进行定义. `mount_mutation`帮助器方法将基于突变的 GraphQL 名称定义一个字段:
```
module Types
class MutationType < BaseObject
include Gitlab::Graphql::MountMutation
graphql_name "Mutation"
mount_mutation Mutations::MergeRequests::SetWip
end
end
```
将生成一个名为`mergeRequestSetWip`的字段, `Mutations::MergeRequests::SetWip`字段将要解决的`Mutations::MergeRequests::SetWip` .
### Authorizing resources[](#authorizing-resources "Permalink")
要授权某个变异内的资源,我们首先要提供所需的变异能力,如下所示:
```
module Mutations
module MergeRequests
class SetWip < Base
graphql_name 'MergeRequestSetWip'
authorize :update_merge_request
end
end
end
```
然后,我们可以致电`authorize!` 在`resolve`方法中,传入我们要验证其功能的资源.
或者,我们可以添加一个`find_object`方法,该方法将在突变上加载对象. 这将允许您使用`authorized_find!` 辅助方法.
当不允许用户执行该操作或找不到对象时,我们应该引发`Gitlab::Graphql::Errors::ResourceNotAvailable`错误. 哪些将正确呈现给客户端.
### Errors in mutations[](#errors-in-mutations "Permalink")
我们鼓励遵循[错误](https://graphql-ruby.org/mutations/mutation_errors)的做法,将其[作为](https://graphql-ruby.org/mutations/mutation_errors)突变的[数据](https://graphql-ruby.org/mutations/mutation_errors) ,从而根据错误的相关者,定义的错误处理者来区分错误.
关键点:
* 所有突变响应都有一个`errors`字段. 如果失败,则应填充此文件;如果成功,则应填充该文件.
* 考虑谁需要看到错误: **用户**还是**开发人员** .
* 客户在执行突变时应始终请求`errors`字段.
* 错误可能会以`$root.errors` (顶级错误)或`$root.data.mutationName.errors` (变异错误)的形式报告给用户. 位置取决于这是什么类型的错误以及它所包含的信息.
考虑一个示例变体`doTheThing` ,该变`doTheThing`返回带有两个字段的响应: `errors: [String]`和`thing: ThingType` . 由于我们正在考虑错误,因此`thing`本身的特定性质与这些示例无关.
突变响应可以处于三种状态:
* [Success](#success)
* [Failure (relevant to the user)](#failure-relevant-to-the-user)
* [Failure (irrelevant to the user)](#failure-irrelevant-to-the-user)
#### Success[](#success "Permalink")
在快乐的道路上, *可能*会返回错误以及预期的有效负载,但是如果一切成功,则`errors`应该是一个空数组,因为没有任何问题需要通知用户.
```
{
data: {
doTheThing: {
errors: [] // if successful, this array will generally be empty.
thing: { .. }
}
}
}
```
#### Failure (relevant to the user)[](#failure-relevant-to-the-user "Permalink")
发生了影响**用户**的错误. 我们将这些称为*突变错误* . 在这种情况下通常没有`thing`来回报:
```
{
data: {
doTheThing: {
errors: ["you cannot touch the thing"],
thing: null
}
}
}
```
例如:
* Model validation errors: the user may need to change the inputs.
* 权限错误:用户需要知道他们不能执行此操作,他们可能需要请求权限或登录.
* 应用程序状态问题阻止了用户的操作,例如:合并冲突,资源被锁定等等.
理想情况下,我们应该防止用户走得太远,但是如果这样做了,则需要告诉他们出了什么问题,以便他们了解失败的原因以及可以实现其意图的方法,即使那很简单重试请求.
可以与变异数据一起返回*可恢复的*错误. 例如,如果用户上载 10 个文件,而其中 3 个失败,其余文件成功,则可以将失败的错误以及有关成功的信息与用户一起使用.
#### Failure (irrelevant to the user)[](#failure-irrelevant-to-the-user "Permalink")
可以在*顶层*返回一个或多个*不可恢复的*错误. 这些是**用户**几乎无法控制的事情,主要应该是**开发人员**需要了解的系统或编程问题. 在这种情况下,没有`data` :
```
{
errors: [
{"message": "argument error: expected an integer, got null"},
]
}
```
这是在突变过程中引发错误的结果. 在我们的实现中,参数错误和验证错误的消息将返回给客户端,所有其他`StandardError`实例将被捕获,记录并呈现给客户端,并将消息设置为`"Internal server error"` . 有关详细信息,请参见[`GraphqlController`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/controllers/graphql_controller.rb) .
这些代表编程错误,例如:
* A GraphQL syntax error, where an `Int` was passed instead of a `String`, or a required argument was not present.
* 我们架构中的错误,例如无法为不可为空的字段提供值.
* 系统错误:例如,Git 存储异常或数据库不可用.
用户在常规使用中不应导致此类错误. 此类错误应视为内部错误,并且不向用户详细显示.
我们需要在突变失败时通知用户,但是我们不必告诉他们原因,因为他们不可能造成突变,尽管我们可以提供重试突变的方法,但他们无能为力.
#### Categorizing errors[](#categorizing-errors "Permalink")
当我们编写突变时,我们需要意识到错误状态属于这两个类别中的哪一个(并与前端开发人员进行沟通以验证我们的假设). 这意味着将*用户*的需求与*客户*的需求区分开.
> *除非用户需要了解错误,否则切勿捕获错误.*
如果用户确实需要了解它,请与前端开发人员进行交流,以确保我们传回的错误信息有用.
另请参见[前端 GraphQL 指南](../development/fe_guide/graphql.html#handling-errors) .
### Aliasing and deprecating mutations[](#aliasing-and-deprecating-mutations "Permalink")
`#mount_aliased_mutation`帮助器允许我们将突变别名作为`MutationType`另一个名称.
例如,将名为`FooMutation`的突变别名为`BarMutation` :
```
mount_aliased_mutation 'BarMutation', Mutations::FooMutation
```
结合[`deprecated`](#deprecating-fields)参数,这使我们可以重命名突变并继续支持旧名称.
Example:
```
mount_aliased_mutation 'UpdateFoo',
Mutations::Foo::Update,
deprecated: { reason: 'Use fooUpdate', milestone: '13.2' }
```
不赞成使用的突变应添加到`Types::DeprecatedMutations`并在`Types::MutationType`的单元测试中进行测试. 合并请求[!34798](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34798)可以称为此示例,包括测试已弃用的别名突变的方法.
## Validating arguments[](#validating-arguments "Permalink")
要验证单个参数,请照常使用[`prepare`选项](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/fields/arguments.md) .
有时,变异或解析器可以接受许多可选参数,但是我们仍然要验证是否至少提供了一个可选参数. 在这种情况下,请考虑使用`#ready?` 突变或解析器中提供验证的方法. `#ready?` 在`#resolve`方法中完成任何工作之前,将调用方法.
Example:
```
def ready?(**args)
if args.values_at(:body, :position).compact.blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'body or position arguments are required'
end
# Always remember to call `#super`
super
end
```
如果将来将此[RFC](https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md)合并,则可以使用`InputUnions`完成.
## GitLab’s custom scalars[](#gitlabs-custom-scalars "Permalink")
### `Types::TimeType`[](#typestimetype "Permalink")
[`Types::TimeType`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/graphql/types/time_type.rb)必须用作处理 Ruby `Time`和`DateTime`对象的所有字段和参数的类型.
该类型是[一个自定义标量](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/type_definitions/scalars.md#custom-scalars) :
* 当用作 GraphQL 字段的类型时,将 Ruby 的`Time`和`DateTime`对象转换为标准化的 ISO-8601 格式的字符串.
* 当用作 GraphQL 参数的类型时,将 ISO-8601 格式的时间字符串转换为 Ruby `Time`对象.
这使我们的 GraphQL API 具有标准化的方式来表示时间并处理时间输入.
Example:
```
field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created'
```
## Testing[](#testing "Permalink")
在`spec/requests/api/graphql`实时进行针对 graphql 查询或突变的*全栈*测试.
添加查询时,可以使用`a working graphql query`共享示例来测试该查询是否呈现有效结果.
使用`GraphqlHelpers#all_graphql_fields_for` -helper,可以构造一个包含所有可用字段的查询. 这使得添加测试渲染所有可能的查询字段变得容易.
为了测试 GraphQL 突变请求, `GraphqlHelpers`提供了 2 个助手: `graphql_mutation` ,它使用突变的名称;以及带有该突变输入的哈希. 这将返回带有变异查询和预备变量的结构.
然后可以将此结构传递给`post_graphql_mutation`帮助器,该帮助器将使用正确的参数发布请求,就像 GraphQL 客户端所做的那样.
要访问突变的响应,可以使用`graphql_mutation_response`帮助器.
使用这些帮助器,我们可以建立如下规格:
```
let(:mutation) do
graphql_mutation(
:merge_request_set_wip,
project_path: 'gitlab-org/gitlab-foss',
iid: '1',
wip: true
)
end
it 'returns a successful response' do
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
expect(graphql_mutation_response(:merge_request_set_wip)['errors']).to be_empty
end
```
## Notes about Query flow and GraphQL infrastructure[](#notes-about-query-flow-and-graphql-infrastructure "Permalink")
可以在`lib/gitlab/graphql`找到 GitLab 的 GraphQL 基础架构.
[检测](https://graphql-ruby.org/queries/instrumentation.html)是环绕正在执行的查询的功能. 它被实现为使用`Instrumentation`类的模块.
Example: `Present`
```
module Gitlab
module Graphql
module Present
#... some code above...
def self.use(schema_definition)
schema_definition.instrument(:field, ::Gitlab::Graphql::Present::Instrumentation.new)
end
end
end
end
```
[查询分析器](https://graphql-ruby.org/queries/ast_analysis.html#analyzer-api)包含一系列回调,以在执行查询之前对其进行验证. 每个字段都可以通过分析仪,最终值也可供您使用.
[多重查询](https://graphql-ruby.org/queries/multiplex.html)使多个查询可以在单个请求中发送. 这减少了发送到服务器的请求数量. (GraphQL Ruby 提供了自定义的 Multiplex 查询分析器和 Multiplex 工具).
### Query limits[](#query-limits "Permalink")
查询和变异受到深度,复杂性和递归的限制,以保护服务器资源免受过度野心或恶意查询的侵害. 这些值可以设置为默认值,并根据需要在特定查询中覆盖. 也可以为每个对象设置复杂度值,并根据返回的对象数来评估最终查询的复杂度. 这对于昂贵的对象(例如需要 Gitaly 调用)很有用.
例如,解析器中的条件复杂度方法:
```
def self.resolver_complexity(args, child_complexity:)
complexity = super
complexity += 2 if args[:labelName]
complexity
end
```
有关复杂性的更多信息: [GraphQL Ruby 文档](https://graphql-ruby.org/queries/complexity_and_depth.html) .
## Documentation and Schema[](#documentation-and-schema "Permalink")
我们的模式位于`app/graphql/gitlab_schema.rb` . 有关详细信息,请参见[架构参考](../api/graphql/reference/index.html) .
模式更改时,需要更新此生成的 GraphQL 文档. 有关生成 GraphQL 文档和架构文件的信息,请参阅[更新架构文档](rake_tasks.html#update-graphql-documentation-and-schema-definitions) .
- GitLab Docs
- Installation
- Requirements
- GitLab cloud native Helm Chart
- Install GitLab with Docker
- Installation from source
- Install GitLab on Microsoft Azure
- Installing GitLab on Google Cloud Platform
- Installing GitLab on Amazon Web Services (AWS)
- Analytics
- Code Review Analytics
- Productivity Analytics
- Value Stream Analytics
- Kubernetes clusters
- Adding and removing Kubernetes clusters
- Adding EKS clusters
- Adding GKE clusters
- Group-level Kubernetes clusters
- Instance-level Kubernetes clusters
- Canary Deployments
- Cluster Environments
- Deploy Boards
- GitLab Managed Apps
- Crossplane configuration
- Cluster management project (alpha)
- Kubernetes Logs
- Runbooks
- Serverless
- Deploying AWS Lambda function using GitLab CI/CD
- Securing your deployed applications
- Groups
- Contribution Analytics
- Custom group-level project templates
- Epics
- Manage epics
- Group Import/Export
- Insights
- Issues Analytics
- Iterations
- Public access
- SAML SSO for GitLab.com groups
- SCIM provisioning using SAML SSO for GitLab.com groups
- Subgroups
- Roadmap
- Projects
- GitLab Secure
- Security Configuration
- Container Scanning
- Dependency Scanning
- Dependency List
- Static Application Security Testing (SAST)
- Secret Detection
- Dynamic Application Security Testing (DAST)
- GitLab Security Dashboard
- Offline environments
- Standalone Vulnerability pages
- Security scanner integration
- Badges
- Bulk editing issues and merge requests at the project level
- Code Owners
- Compliance
- License Compliance
- Compliance Dashboard
- Create a project
- Description templates
- Deploy Keys
- Deploy Tokens
- File finder
- Project integrations
- Integrations
- Atlassian Bamboo CI Service
- Bugzilla Service
- Custom Issue Tracker service
- Discord Notifications service
- Enabling emails on push
- GitHub project integration
- Hangouts Chat service
- Atlassian HipChat
- Irker IRC Gateway
- GitLab Jira integration
- Mattermost Notifications Service
- Mattermost slash commands
- Microsoft Teams service
- Mock CI Service
- Prometheus integration
- Redmine Service
- Slack Notifications Service
- Slack slash commands
- GitLab Slack application
- Webhooks
- YouTrack Service
- Insights
- Issues
- Crosslinking Issues
- Design Management
- Confidential issues
- Due dates
- Issue Boards
- Issue Data and Actions
- Labels
- Managing issues
- Milestones
- Multiple Assignees for Issues
- Related issues
- Service Desk
- Sorting and ordering issue lists
- Issue weight
- Associate a Zoom meeting with an issue
- Merge requests
- Allow collaboration on merge requests across forks
- Merge Request Approvals
- Browser Performance Testing
- How to create a merge request
- Cherry-pick changes
- Code Quality
- Load Performance Testing
- Merge Request dependencies
- Fast-forward merge requests
- Merge when pipeline succeeds
- Merge request conflict resolution
- Reverting changes
- Reviewing and managing merge requests
- Squash and merge
- Merge requests versions
- Draft merge requests
- Members of a project
- Migrating projects to a GitLab instance
- Import your project from Bitbucket Cloud to GitLab
- Import your project from Bitbucket Server to GitLab
- Migrating from ClearCase
- Migrating from CVS
- Import your project from FogBugz to GitLab
- Gemnasium
- Import your project from GitHub to GitLab
- Project importing from GitLab.com to your private GitLab instance
- Import your project from Gitea to GitLab
- Import your Jira project issues to GitLab
- Migrating from Perforce Helix
- Import Phabricator tasks into a GitLab project
- Import multiple repositories by uploading a manifest file
- Import project from repo by URL
- Migrating from SVN to GitLab
- Migrating from TFVC to Git
- Push Options
- Releases
- Repository
- Branches
- Git Attributes
- File Locking
- Git file blame
- Git file history
- Repository mirroring
- Protected branches
- Protected tags
- Push Rules
- Reduce repository size
- Signing commits with GPG
- Syntax Highlighting
- GitLab Web Editor
- Web IDE
- Requirements Management
- Project settings
- Project import/export
- Project access tokens (Alpha)
- Share Projects with other Groups
- Snippets
- Static Site Editor
- Wiki
- Project operations
- Monitor metrics for your CI/CD environment
- Set up alerts for Prometheus metrics
- Embedding metric charts within GitLab-flavored Markdown
- Embedding Grafana charts
- Using the Metrics Dashboard
- Dashboard YAML properties
- Metrics dashboard settings
- Panel types for dashboards
- Using Variables
- Templating variables for metrics dashboards
- Prometheus Metrics library
- Monitoring AWS Resources
- Monitoring HAProxy
- Monitoring Kubernetes
- Monitoring NGINX
- Monitoring NGINX Ingress Controller
- Monitoring NGINX Ingress Controller with VTS metrics
- Alert Management
- Error Tracking
- Tracing
- Incident Management
- GitLab Status Page
- Feature Flags
- GitLab CI/CD
- GitLab CI/CD pipeline configuration reference
- GitLab CI/CD include examples
- Introduction to CI/CD with GitLab
- Getting started with GitLab CI/CD
- How to enable or disable GitLab CI/CD
- Using SSH keys with GitLab CI/CD
- Migrating from CircleCI
- Migrating from Jenkins
- Auto DevOps
- Getting started with Auto DevOps
- Requirements for Auto DevOps
- Customizing Auto DevOps
- Stages of Auto DevOps
- Upgrading PostgreSQL for Auto DevOps
- Cache dependencies in GitLab CI/CD
- GitLab ChatOps
- Cloud deployment
- Docker integration
- Building Docker images with GitLab CI/CD
- Using Docker images
- Building images with kaniko and GitLab CI/CD
- GitLab CI/CD environment variables
- Predefined environment variables reference
- Where variables can be used
- Deprecated GitLab CI/CD variables
- Environments and deployments
- Protected Environments
- GitLab CI/CD Examples
- Test a Clojure application with GitLab CI/CD
- Using Dpl as deployment tool
- Testing a Phoenix application with GitLab CI/CD
- End-to-end testing with GitLab CI/CD and WebdriverIO
- DevOps and Game Dev with GitLab CI/CD
- Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
- How to deploy Maven projects to Artifactory with GitLab CI/CD
- Testing PHP projects
- Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD
- Test and deploy Laravel applications with GitLab CI/CD and Envoy
- Test and deploy a Python application with GitLab CI/CD
- Test and deploy a Ruby application with GitLab CI/CD
- Test and deploy a Scala application to Heroku
- GitLab CI/CD for external repositories
- Using GitLab CI/CD with a Bitbucket Cloud repository
- Using GitLab CI/CD with a GitHub repository
- GitLab Pages
- GitLab Pages
- GitLab Pages domain names, URLs, and baseurls
- Create a GitLab Pages website from scratch
- Custom domains and SSL/TLS Certificates
- GitLab Pages integration with Let's Encrypt
- GitLab Pages Access Control
- Exploring GitLab Pages
- Incremental Rollouts with GitLab CI/CD
- Interactive Web Terminals
- Optimizing GitLab for large repositories
- Metrics Reports
- CI/CD pipelines
- Pipeline Architecture
- Directed Acyclic Graph
- Multi-project pipelines
- Parent-child pipelines
- Pipelines for Merge Requests
- Pipelines for Merged Results
- Merge Trains
- Job artifacts
- Pipeline schedules
- Pipeline settings
- Triggering pipelines through the API
- Review Apps
- Configuring GitLab Runners
- GitLab CI services examples
- Using MySQL
- Using PostgreSQL
- Using Redis
- Troubleshooting CI/CD
- GitLab Package Registry
- GitLab Container Registry
- Dependency Proxy
- GitLab Composer Repository
- GitLab Conan Repository
- GitLab Maven Repository
- GitLab NPM Registry
- GitLab NuGet Repository
- GitLab PyPi Repository
- API Docs
- API resources
- .gitignore API
- GitLab CI YMLs API
- Group and project access requests API
- Appearance API
- Applications API
- Audit Events API
- Avatar API
- Award Emoji API
- Project badges API
- Group badges API
- Branches API
- Broadcast Messages API
- Project clusters API
- Group clusters API
- Instance clusters API
- Commits API
- Container Registry API
- Custom Attributes API
- Dashboard annotations API
- Dependencies API
- Deploy Keys API
- Deployments API
- Discussions API
- Dockerfiles API
- Environments API
- Epics API
- Events
- Feature Flags API
- Feature flag user lists API
- Freeze Periods API
- Geo Nodes API
- Group Activity Analytics API
- Groups API
- Import API
- Issue Boards API
- Group Issue Boards API
- Issues API
- Epic Issues API
- Issues Statistics API
- Jobs API
- Keys API
- Labels API
- Group Labels API
- License
- Licenses API
- Issue links API
- Epic Links API
- Managed Licenses API
- Markdown API
- Group and project members API
- Merge request approvals API
- Merge requests API
- Project milestones API
- Group milestones API
- Namespaces API
- Notes API
- Notification settings API
- Packages API
- Pages domains API
- Pipeline schedules API
- Pipeline triggers API
- Pipelines API
- Project Aliases API
- Project import/export API
- Project repository storage moves API
- Project statistics API
- Project templates API
- Projects API
- Protected branches API
- Protected tags API
- Releases API
- Release links API
- Repositories API
- Repository files API
- Repository submodules API
- Resource label events API
- Resource milestone events API
- Resource weight events API
- Runners API
- SCIM API
- Search API
- Services API
- Application settings API
- Sidekiq Metrics API
- Snippets API
- Project snippets
- Application statistics API
- Suggest Changes API
- System hooks API
- Tags API
- Todos API
- Users API
- Project-level Variables API
- Group-level Variables API
- Version API
- Vulnerabilities API
- Vulnerability Findings API
- Wikis API
- GraphQL API
- Getting started with GitLab GraphQL API
- GraphQL API Resources
- API V3 to API V4
- Validate the .gitlab-ci.yml (API)
- User Docs
- Abuse reports
- User account
- Active sessions
- Deleting a User account
- Permissions
- Personal access tokens
- Profile preferences
- Threads
- GitLab and SSH keys
- GitLab integrations
- Git
- GitLab.com settings
- Infrastructure as code with Terraform and GitLab
- GitLab keyboard shortcuts
- GitLab Markdown
- AsciiDoc
- GitLab Notification Emails
- GitLab Quick Actions
- Autocomplete characters
- Reserved project and group names
- Search through GitLab
- Advanced Global Search
- Advanced Syntax Search
- Time Tracking
- GitLab To-Do List
- Administrator Docs
- Reference architectures
- Reference architecture: up to 1,000 users
- Reference architecture: up to 2,000 users
- Reference architecture: up to 3,000 users
- Reference architecture: up to 5,000 users
- Reference architecture: up to 10,000 users
- Reference architecture: up to 25,000 users
- Reference architecture: up to 50,000 users
- Troubleshooting a reference architecture set up
- Working with the bundled Consul service
- Configuring PostgreSQL for scaling
- Configuring GitLab application (Rails)
- Load Balancer for multi-node GitLab
- Configuring a Monitoring node for Scaling and High Availability
- NFS
- Working with the bundled PgBouncer service
- Configuring Redis for scaling
- Configuring Sidekiq
- Admin Area settings
- Continuous Integration and Deployment Admin settings
- Custom instance-level project templates
- Diff limits administration
- Enable and disable GitLab features deployed behind feature flags
- Geo nodes Admin Area
- GitLab Pages administration
- Health Check
- Job logs
- Labels administration
- Log system
- PlantUML & GitLab
- Repository checks
- Repository storage paths
- Repository storage types
- Account and limit settings
- Service templates
- System hooks
- Changing your time zone
- Uploads administration
- Abuse reports
- Activating and deactivating users
- Audit Events
- Blocking and unblocking users
- Broadcast Messages
- Elasticsearch integration
- Gitaly
- Gitaly Cluster
- Gitaly reference
- Monitoring GitLab
- Monitoring GitLab with Prometheus
- Performance Bar
- Usage statistics
- Object Storage
- Performing Operations in GitLab
- Cleaning up stale Redis sessions
- Fast lookup of authorized SSH keys in the database
- Filesystem Performance Benchmarking
- Moving repositories managed by GitLab
- Run multiple Sidekiq processes
- Sidekiq MemoryKiller
- Switching to Puma
- Understanding Unicorn and unicorn-worker-killer
- User lookup via OpenSSH's AuthorizedPrincipalsCommand
- GitLab Package Registry administration
- GitLab Container Registry administration
- Replication (Geo)
- Geo database replication
- Geo with external PostgreSQL instances
- Geo configuration
- Using a Geo Server
- Updating the Geo nodes
- Geo with Object storage
- Docker Registry for a secondary node
- Geo for multiple nodes
- Geo security review (Q&A)
- Location-aware Git remote URL with AWS Route53
- Tuning Geo
- Removing secondary Geo nodes
- Geo data types support
- Geo Frequently Asked Questions
- Geo Troubleshooting
- Geo validation tests
- Disaster Recovery (Geo)
- Disaster recovery for planned failover
- Bring a demoted primary node back online
- Automatic background verification
- Rake tasks
- Back up and restore GitLab
- Clean up
- Namespaces
- Maintenance Rake tasks
- Geo Rake Tasks
- GitHub import
- Import bare repositories
- Integrity check Rake task
- LDAP Rake tasks
- Listing repository directories
- Praefect Rake tasks
- Project import/export administration
- Repository storage Rake tasks
- Generate sample Prometheus data
- Uploads migrate Rake tasks
- Uploads sanitize Rake tasks
- User management
- Webhooks administration
- X.509 signatures
- Server hooks
- Static objects external storage
- Updating GitLab
- GitLab release and maintenance policy
- Security
- Password Storage
- Custom password length limits
- Restrict allowed SSH key technologies and minimum length
- Rate limits
- Webhooks and insecure internal web services
- Information exclusivity
- How to reset your root password
- How to unlock a locked user from the command line
- User File Uploads
- How we manage the TLS protocol CRIME vulnerability
- User email confirmation at sign-up
- Security of running jobs
- Proxying assets
- CI/CD Environment Variables
- Contributor and Development Docs
- Contribute to GitLab
- Community members & roles
- Implement design & UI elements
- Issues workflow
- Merge requests workflow
- Code Review Guidelines
- Style guides
- GitLab Architecture Overview
- CI/CD development documentation
- Database guides
- Database Review Guidelines
- Database Review Guidelines
- Migration Style Guide
- What requires downtime?
- Understanding EXPLAIN plans
- Rake tasks for developers
- Mass inserting Rails models
- GitLab Documentation guidelines
- Documentation Style Guide
- Documentation structure and template
- Documentation process
- Documentation site architecture
- Global navigation
- GitLab Docs monthly release process
- Telemetry Guide
- Usage Ping Guide
- Snowplow Guide
- Experiment Guide
- Feature flags in development of GitLab
- Feature flags process
- Developing with feature flags
- Feature flag controls
- Document features deployed behind feature flags
- Frontend Development Guidelines
- Accessibility & Readability
- Ajax
- Architecture
- Axios
- Design Patterns
- Frontend Development Process
- DropLab
- Emojis
- Filter
- Frontend FAQ
- GraphQL
- Icons and SVG Illustrations
- InputSetter
- Performance
- Principles
- Security
- Tooling
- Vuex
- Vue
- Geo (development)
- Geo self-service framework (alpha)
- Gitaly developers guide
- GitLab development style guides
- API style guide
- Go standards and style guidelines
- GraphQL API style guide
- Guidelines for shell commands in the GitLab codebase
- HTML style guide
- JavaScript style guide
- Migration Style Guide
- Newlines style guide
- Python Development Guidelines
- SCSS style guide
- Shell scripting standards and style guidelines
- Sidekiq debugging
- Sidekiq Style Guide
- SQL Query Guidelines
- Vue.js style guide
- Instrumenting Ruby code
- Testing standards and style guidelines
- Flaky tests
- Frontend testing standards and style guidelines
- GitLab tests in the Continuous Integration (CI) context
- Review Apps
- Smoke Tests
- Testing best practices
- Testing levels
- Testing Rails migrations at GitLab
- Testing Rake tasks
- End-to-end Testing
- Beginner's guide to writing end-to-end tests
- End-to-end testing Best Practices
- Dynamic Element Validation
- Flows in GitLab QA
- Page objects in GitLab QA
- Resource class in GitLab QA
- Style guide for writing end-to-end tests
- Testing with feature flags
- Translate GitLab to your language
- Internationalization for GitLab
- Translating GitLab
- Proofread Translations
- Merging translations from CrowdIn
- Value Stream Analytics development guide
- GitLab subscription
- Activate GitLab EE with a license