<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 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) .
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看