diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-29 15:10:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-29 15:10:00 +0300 |
commit | 4233d3aa86fe94e6288279aa55d42ed95bfe753c (patch) | |
tree | 7b97b519371f6df1fa6a0f2ffe69535207a73754 /doc | |
parent | e357d4951c53a3ce4f696cf533ce24a4c6350a7e (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'doc')
-rw-r--r-- | doc/api/branches.md | 5 | ||||
-rw-r--r-- | doc/api/graphql/reference/gitlab_schema.graphql | 135 | ||||
-rw-r--r-- | doc/api/graphql/reference/gitlab_schema.json | 351 | ||||
-rw-r--r-- | doc/api/graphql/reference/index.md | 15 | ||||
-rw-r--r-- | doc/api/projects.md | 4 | ||||
-rw-r--r-- | doc/development/fe_guide/vue.md | 13 | ||||
-rw-r--r-- | doc/development/fe_guide/vue3_migration.md | 109 | ||||
-rw-r--r-- | doc/development/geo/framework.md | 223 | ||||
-rw-r--r-- | doc/user/project/settings/index.md | 2 |
9 files changed, 807 insertions, 50 deletions
diff --git a/doc/api/branches.md b/doc/api/branches.md index 2f9ca62ced6..f9c87222f9a 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -41,6 +41,7 @@ Example response: "developers_can_push": false, "developers_can_merge": false, "can_push": true, + "web_url": "http://gitlab.example.com/my-group/my-project/-/tree/master", "commit": { "author_email": "john@example.com", "author_name": "John Smith", @@ -96,6 +97,7 @@ Example response: "developers_can_push": false, "developers_can_merge": false, "can_push": true, + "web_url": "http://gitlab.example.com/my-group/my-project/-/tree/master", "commit": { "author_email": "john@example.com", "author_name": "John Smith", @@ -171,7 +173,8 @@ Example response: "default": false, "developers_can_push": false, "developers_can_merge": false, - "can_push": true + "can_push": true, + "web_url": "http://gitlab.example.com/my-group/my-project/-/tree/newbranch" } ``` diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 0a56eb9197c..604322bf5a4 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -3451,6 +3451,36 @@ type GeoNode { name: String """ + Package file registries of the GeoNode. Available only when feature flag `geo_self_service_framework` is enabled + """ + packageFileRegistries( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Filters registries by their ID + """ + ids: [ID!] + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): PackageFileRegistryConnection + + """ Indicates whether this Geo node is the primary """ primary: Boolean @@ -6330,6 +6360,86 @@ interface Noteable { } """ +Represents the sync and verification state of a package file +""" +type PackageFileRegistry { + """ + Timestamp when the PackageFileRegistry was created + """ + createdAt: Time + + """ + ID of the PackageFileRegistry + """ + id: ID! + + """ + Error message during sync of the PackageFileRegistry + """ + lastSyncFailure: String + + """ + Timestamp of the most recent successful sync of the PackageFileRegistry + """ + lastSyncedAt: Time + + """ + ID of the PackageFile + """ + packageFileId: ID! + + """ + Timestamp after which the PackageFileRegistry should be resynced + """ + retryAt: Time + + """ + Number of consecutive failed sync attempts of the PackageFileRegistry + """ + retryCount: Int + + """ + Sync state of the PackageFileRegistry + """ + state: RegistryState +} + +""" +The connection type for PackageFileRegistry. +""" +type PackageFileRegistryConnection { + """ + A list of edges. + """ + edges: [PackageFileRegistryEdge] + + """ + A list of nodes. + """ + nodes: [PackageFileRegistry] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An edge in a connection. +""" +type PackageFileRegistryEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: PackageFileRegistry +} + +""" Information about pagination in a connection. """ type PageInfo { @@ -7806,6 +7916,31 @@ type Query { } """ +State of a Geo registry. +""" +enum RegistryState { + """ + Registry that failed to sync + """ + FAILED + + """ + Registry waiting to be synced + """ + PENDING + + """ + Registry currently syncing + """ + STARTED + + """ + Registry that is synced + """ + SYNCED +} + +""" Autogenerated input type of RemoveAwardEmoji """ input RemoveAwardEmojiInput { diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index b422258171f..d41088e051d 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -9927,6 +9927,77 @@ "deprecationReason": null }, { + "name": "packageFileRegistries", + "description": "Package file registries of the GeoNode. Available only when feature flag `geo_self_service_framework` is enabled", + "args": [ + { + "name": "ids", + "description": "Filters registries by their ID", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + "defaultValue": null + }, + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "PackageFileRegistryConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "primary", "description": "Indicates whether this Geo node is the primary", "args": [ @@ -19093,6 +19164,251 @@ }, { "kind": "OBJECT", + "name": "PackageFileRegistry", + "description": "Represents the sync and verification state of a package file", + "fields": [ + { + "name": "createdAt", + "description": "Timestamp when the PackageFileRegistry was created", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Time", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": "ID of the PackageFileRegistry", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSyncFailure", + "description": "Error message during sync of the PackageFileRegistry", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSyncedAt", + "description": "Timestamp of the most recent successful sync of the PackageFileRegistry", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Time", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "packageFileId", + "description": "ID of the PackageFile", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "retryAt", + "description": "Timestamp after which the PackageFileRegistry should be resynced", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Time", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "retryCount", + "description": "Number of consecutive failed sync attempts of the PackageFileRegistry", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "state", + "description": "Sync state of the PackageFileRegistry", + "args": [ + + ], + "type": { + "kind": "ENUM", + "name": "RegistryState", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "PackageFileRegistryConnection", + "description": "The connection type for PackageFileRegistry.", + "fields": [ + { + "name": "edges", + "description": "A list of edges.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PackageFileRegistryEdge", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "A list of nodes.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PackageFileRegistry", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "Information to aid in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "PackageFileRegistryEdge", + "description": "An edge in a connection.", + "fields": [ + { + "name": "cursor", + "description": "A cursor for use in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "node", + "description": "The item at the end of the edge.", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "PackageFileRegistry", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", "name": "PageInfo", "description": "Information about pagination in a connection.", "fields": [ @@ -23240,6 +23556,41 @@ "possibleTypes": null }, { + "kind": "ENUM", + "name": "RegistryState", + "description": "State of a Geo registry.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "PENDING", + "description": "Registry waiting to be synced", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "STARTED", + "description": "Registry currently syncing", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SYNCED", + "description": "Registry that is synced", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FAILED", + "description": "Registry that failed to sync", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { "kind": "INPUT_OBJECT", "name": "RemoveAwardEmojiInput", "description": "Autogenerated input type of RemoveAwardEmoji", diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index a974278f04f..82f5bd1e000 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -962,6 +962,21 @@ Represents a milestone. | `readNote` | Boolean! | Indicates the user can perform `read_note` on this resource | | `resolveNote` | Boolean! | Indicates the user can perform `resolve_note` on this resource | +## PackageFileRegistry + +Represents the sync and verification state of a package file + +| Name | Type | Description | +| --- | ---- | ---------- | +| `createdAt` | Time | Timestamp when the PackageFileRegistry was created | +| `id` | ID! | ID of the PackageFileRegistry | +| `lastSyncFailure` | String | Error message during sync of the PackageFileRegistry | +| `lastSyncedAt` | Time | Timestamp of the most recent successful sync of the PackageFileRegistry | +| `packageFileId` | ID! | ID of the PackageFile | +| `retryAt` | Time | Timestamp after which the PackageFileRegistry should be resynced | +| `retryCount` | Int | Number of consecutive failed sync attempts of the PackageFileRegistry | +| `state` | RegistryState | Sync state of the PackageFileRegistry | + ## PageInfo Information about pagination in a connection. diff --git a/doc/api/projects.md b/doc/api/projects.md index b9513cc767e..aa5e9ef6e43 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -312,8 +312,8 @@ GET /projects?custom_attributes[key]=value&custom_attributes[other_key]=other_va ### Pagination limits -From GitLab 12.10, [offset-based pagination](README.md#offset-based-pagination) will be -[limited to 10,000 records](https://gitlab.com/gitlab-org/gitlab/issues/34565). +From GitLab 13.0, [offset-based pagination](README.md#offset-based-pagination) will be +[limited to 50,000 records](https://gitlab.com/gitlab-org/gitlab/issues/34565). [Keyset pagination](README.md#keyset-based-pagination) will be required to retrieve projects beyond this limit. diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md index c8aec5601a2..7e2d4b08767 100644 --- a/doc/development/fe_guide/vue.md +++ b/doc/development/fe_guide/vue.md @@ -289,3 +289,16 @@ One should apply to be a Vue.js expert by opening an MR when the Merge Request's - Full understanding of testing a Vue and Vuex application - Vuex code follows the [documented pattern](vuex.md#actions-pattern-request-and-receive-namespaces) - Knowledge about the existing Vue and Vuex applications and existing reusable components + +## Vue 2 -> Vue 3 Migration + +> This section is added temporarily to support the efforts to migrate the codebase from Vue 2.x to Vue 3.x + +Currently, we recommend to minimize adding certain features to the codebase to prevent increasing the tech debt for the eventual migration: + +- filters; +- event buses; +- functional templated +- `slot` attributes + +You can find more details on [Migration to Vue 3](vue3_migration.md) diff --git a/doc/development/fe_guide/vue3_migration.md b/doc/development/fe_guide/vue3_migration.md new file mode 100644 index 00000000000..1292926d951 --- /dev/null +++ b/doc/development/fe_guide/vue3_migration.md @@ -0,0 +1,109 @@ +# Migration to Vue 3 + +In order to prepare for the eventual migration to Vue 3.x, we should be wary about adding the following features to the codebase: + +## Vue filters + +**Why?** + +Filters [are removed](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md) from the Vue 3 API completely. + +**What to use instead** + +Component's computed properties / methods or external helpers. + +## Event bus + +**Why?** + +`$on` and `$off` methods [are removed](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0020-events-api-change.md) from the Vue instance, so in Vue 3 it can't be used to create an event bus. + +**What to use instead** + +Vue docs recommend using [mitt](https://github.com/developit/mitt) library. It's relatively small (200 bytes gzipped) and has a simple API: + +```javascript +import mitt from 'mitt' + +const emitter = mitt() + +// listen to an event +emitter.on('foo', e => console.log('foo', e) ) + +// listen to all events +emitter.on('*', (type, e) => console.log(type, e) ) + +// fire an event +emitter.emit('foo', { a: 'b' }) + +// working with handler references: +function onFoo() {} + +emitter.on('foo', onFoo) // listen +emitter.off('foo', onFoo) // unlisten +``` + +## <template functional> + +**Why?** + +In Vue 3, `{ functional: true }` option [is removed](https://github.com/vuejs/rfcs/blob/functional-async-api-change/active-rfcs/0007-functional-async-api-change.md) and `<template functional>` is no longer supported. + +**What to use instead** + +Functional components must be written as plain functions: + +```javascript +import { h } from 'vue' + +const FunctionalComp = (props, slots) => { + return h('div', `Hello! ${props.name}`) +} +``` + +## Old slots syntax with `slot` attribute + +**Why?** + +In Vue 2.6 `slot` attribute was already deprecated in favor of `v-slot` directive but its usage is still allowed and sometimes we prefer using them because it simplifies unit tests (with old syntax, slots are rendered on `shallowMount`). However, in Vue 3 we can't use old syntax anymore. + +**What to use instead** + +The syntax with `v-slot` directive. To fix rendering slots in `shallowMount`, we need to stub a child component with slots explicitly. + +```html +<!-- MyAwesomeComponent.vue --> +<script> +import SomeChildComponent from './some_child_component.vue' + +export default { + components: { + SomeChildComponent + } +} + +</script> + +<template> + <div> + <h1>Hello GitLab!</h1> + <some-child-component> + <template #header> + Header content + </template> + </some-child-component> + </div> +</template> +``` + +```js +// MyAwesomeComponent.spec.js + +import SomeChildComponent from '~/some_child_component.vue' + +shallowMount(MyAwesomeComponent, { + stubs: { + SomeChildComponent + } +}) +``` diff --git a/doc/development/geo/framework.md b/doc/development/geo/framework.md index 83809d1fd3d..a2ee52cbc7c 100644 --- a/doc/development/geo/framework.md +++ b/doc/development/geo/framework.md @@ -161,49 +161,7 @@ state. For example, to add support for files referenced by a `Widget` model with a `widgets` table, you would perform the following steps: -1. Add verification state fields to the `widgets` table so the Geo primary can - track verification state: - - ```ruby - # frozen_string_literal: true - - class AddVerificationStateToWidgets < ActiveRecord::Migration[6.0] - DOWNTIME = false - - def change - add_column :widgets, :verification_retry_at, :datetime_with_timezone - add_column :widgets, :verified_at, :datetime_with_timezone - add_column :widgets, :verification_checksum, :string - add_column :widgets, :verification_failure, :string - add_column :widgets, :verification_retry_count, :integer - end - end - ``` - -1. Add a partial index on `verification_failure` and `verification_checksum` to ensure - re-verification can be performed efficiently: - - ```ruby - # frozen_string_literal: true - - class AddVerificationFailureIndexToWidgets < ActiveRecord::Migration[6.0] - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - - disable_ddl_transaction! - - def up - add_concurrent_index :widgets, :verification_failure, where: "(verification_failure IS NOT NULL)", name: "widgets_verification_failure_partial" - add_concurrent_index :widgets, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "widgets_verification_checksum_partial" - end - - def down - remove_concurrent_index :widgets, :verification_failure - remove_concurrent_index :widgets, :verification_checksum - end - end - ``` +#### Replication 1. Include `Gitlab::Geo::ReplicableModel` in the `Widget` class, and specify the Replicator class `with_replicator Geo::WidgetReplicator`. @@ -350,11 +308,53 @@ For example, to add support for files referenced by a `Widget` model with a end ``` -Widget files should now be replicated and verified by Geo! +Widgets should now be replicated by Geo! + +#### Verification + +1. Add verification state fields to the `widgets` table so the Geo primary can + track verification state: + + ```ruby + # frozen_string_literal: true + + class AddVerificationStateToWidgets < ActiveRecord::Migration[6.0] + DOWNTIME = false -### Verification statistics with Blob Replicator Strategy + def change + add_column :widgets, :verification_retry_at, :datetime_with_timezone + add_column :widgets, :verified_at, :datetime_with_timezone + add_column :widgets, :verification_checksum, :string + add_column :widgets, :verification_failure, :string + add_column :widgets, :verification_retry_count, :integer + end + end + ``` -GitLab Geo stores statistic data in the `geo_node_statuses` table. +1. Add a partial index on `verification_failure` and `verification_checksum` to ensure + re-verification can be performed efficiently: + + ```ruby + # frozen_string_literal: true + + class AddVerificationFailureIndexToWidgets < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :widgets, :verification_failure, where: "(verification_failure IS NOT NULL)", name: "widgets_verification_failure_partial" + add_concurrent_index :widgets, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "widgets_verification_checksum_partial" + end + + def down + remove_concurrent_index :widgets, :verification_failure + remove_concurrent_index :widgets, :verification_checksum + end + end + ``` 1. Add fields `widget_count`, `widget_checksummed_count`, and `widget_checksum_failed_count` to `GeoNodeStatus#RESOURCE_STATUS_FIELDS` array in `ee/app/models/geo_node_status.rb`. @@ -378,3 +378,134 @@ GitLab Geo stores statistic data in the `geo_node_statuses` table. 1. Update `Sidekiq metrics` table in `doc/administration/monitoring/prometheus/gitlab_metrics.md` with new fields. 1. Update `GET /geo_nodes/status` example response in `doc/api/geo_nodes.md` with new fields. 1. Update `ee/spec/models/geo_node_status_spec.rb` and `ee/spec/factories/geo_node_statuses.rb` with new fields. + +To do: Add verification on secondaries. + +Widgets should now be verified by Geo! + +#### GraphQL API + +1. Add a new field to `GeoNodeType` in + `ee/app/graphql/types/geo/geo_node_type.rb`: + + ```ruby + field :widget_registries, ::Types::Geo::WidgetRegistryType.connection_type, + null: true, + resolver: ::Resolvers::Geo::WidgetRegistriesResolver, + description: 'Find widget registries on this Geo node', + feature_flag: :geo_self_service_framework + ``` + +1. Add the new `widget_registries` field name to the `expected_fields` array in + `ee/spec/graphql/types/geo/geo_node_type_spec.rb`. + +1. Create `ee/app/graphql/resolvers/geo/widget_registries_resolver.rb`: + + ```ruby + # frozen_string_literal: true + + module Resolvers + module Geo + class WidgetRegistriesResolver < BaseResolver + include RegistriesResolver + end + end + end + ``` + +1. Create `ee/spec/graphql/resolvers/geo/widget_registries_resolver_spec.rb`: + + ```ruby + # frozen_string_literal: true + + require 'spec_helper' + + describe Resolvers::Geo::WidgetRegistriesResolver do + it_behaves_like 'a Geo registries resolver', :widget_registry + end + ``` + +1. Create `ee/app/finders/geo/widget_registry_finder.rb`: + + ```ruby + # frozen_string_literal: true + + module Geo + class WidgetRegistryFinder + include FrameworkRegistryFinder + end + end + ``` + +1. Create `ee/spec/finders/geo/widget_registry_finder_spec.rb`: + + ```ruby + # frozen_string_literal: true + + require 'spec_helper' + + describe Geo::WidgetRegistryFinder do + it_behaves_like 'a framework registry finder', :widget_registry + end + ``` + +1. Create `ee/app/graphql/types/geo/package_file_registry_type.rb`: + + ```ruby + # frozen_string_literal: true + + module Types + module Geo + # rubocop:disable Graphql/AuthorizeTypes because it is included + class WidgetRegistryType < BaseObject + include ::Types::Geo::RegistryType + + graphql_name 'WidgetRegistry' + description 'Represents the sync and verification state of a widget' + + field :widget_id, GraphQL::ID_TYPE, null: false, description: 'ID of the Widget' + end + end + end + ``` + +1. Create `ee/spec/graphql/types/geo/widget_registry_type_spec.rb`: + + ```ruby + # frozen_string_literal: true + + require 'spec_helper' + + describe GitlabSchema.types['WidgetRegistry'] do + it_behaves_like 'a Geo registry type' + + it 'has the expected fields (other than those included in RegistryType)' do + expected_fields = %i[widget_id] + + expect(described_class).to have_graphql_fields(*expected_fields).at_least + end + end + ``` + +1. Add integration tests for providing Widget registry data to the frontend via + the GraphQL API, by duplicating and modifying the following shared examples + in `ee/spec/requests/api/graphql/geo/registries_spec.rb`: + + ```ruby + it_behaves_like 'gets registries for', { + field_name: 'widgetRegistries', + registry_class_name: 'WidgetRegistry', + registry_factory: :widget_registry, + registry_foreign_key_field_name: 'widgetId' + } + ``` + +Individual widget synchronization and verification data should now be available +via the GraphQL API! + +#### Admin UI + +To do. + +Widget sync and verification data (aggregate and individual) should now be +available in the Admin UI! diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 6cb97e2e2a8..ae87945c684 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -257,7 +257,7 @@ To do so: 1. Confirm the action by typing the project's path as instructed. NOTE: **Note:** -Only project maintainers have the [permissions](../../permissions.md#project-members-permissions) +Only project owners have the [permissions](../../permissions.md#project-members-permissions) to remove a fork relationship. ## Operations settings |