From 09ff71d425b83646eed07d52078111ccbd4b84a9 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 29 Mar 2021 15:09:30 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../issue_templates/Feature Flag Roll Out.md | 110 ++- .../pipelines/components/graph/job_item.vue | 6 +- .../components/pipelines_list/job_item.vue | 6 +- .../queries/get_dag_vis_data.query.graphql | 1 + app/graphql/types/query_type.rb | 11 +- app/graphql/types/sort_enum.rb | 32 +- app/graphql/types/user_type.rb | 2 +- app/helpers/sidebars_helper.rb | 30 + app/models/note.rb | 1 + app/models/packages/package.rb | 2 +- app/models/protected_branch.rb | 2 +- .../users/in_product_marketing_email.rb | 15 +- app/presenters/search_service_presenter.rb | 3 +- app/services/ci/pipeline_trigger_service.rb | 17 + .../in_product_marketing_emails_service.rb | 31 +- app/views/dashboard/merge_requests.html.haml | 4 +- .../layouts/nav/sidebar/_group.html.haml | 2 +- .../layouts/nav/sidebar/_profile.html.haml | 2 +- .../layouts/nav/sidebar/_project.html.haml | 2 +- .../shared/_branches_list.html.haml | 2 +- .../shared/_create_protected_branch.html.haml | 2 +- .../_update_protected_branch.html.haml | 2 +- ...ow_force_push_to_protected_branches_ff.yml | 5 + .../unreleased/325937-fix-notes-n-plus-1.yml | 5 + ...tlab-docs-deprecation-use-schema-types.yml | 5 + ...change-payload-format-for-dora-metrics.yml | 5 + .../eb-sort-code-quality-mr-widget.yml | 5 + ...maven-query-optimization-ff-by-default.yml | 5 + ...x-tooltip-position-mini-pipeline-chart.yml | 5 + .../notes_quick_action_feedback.yml | 5 + changelogs/unreleased/revert-0767b9e2.yml | 5 + ...allow_force_push_to_protected_branches.yml | 2 +- ...tadata_by_path_with_optimization_fence.yml | 2 +- ...metheus_metrics_method_instrumentation.yml | 8 - ...rometheus_metrics_view_instrumentation.yml | 8 - doc/api/dora/metrics.md | 32 +- doc/api/graphql/reference/index.md | 792 +++++++++--------- doc/user/project/protected_branches.md | 15 +- lib/feature.rb | 8 +- lib/gitlab/ci/reports/codequality_reports.rb | 7 + .../reports/codequality_reports_comparer.rb | 5 + lib/gitlab/graphql/docs/helper.rb | 134 ++- lib/gitlab/graphql/docs/renderer.rb | 7 +- .../graphql/docs/templates/default.md.haml | 18 +- .../quick_actions/command_definition.rb | 11 +- lib/tasks/gitlab/graphql.rake | 4 +- locale/gitlab.pot | 3 + .../ci/reports/codequality_degradations.rb | 43 + spec/features/dashboard/shortcuts_spec.rb | 2 +- spec/fixtures/emails/update_commands_only.eml | 22 + spec/graphql/features/feature_flag_spec.rb | 1 - spec/graphql/types/base_field_spec.rb | 1 - spec/helpers/sidebars_helper_spec.rb | 45 + spec/lib/api/helpers_spec.rb | 1 - spec/lib/feature/gitaly_spec.rb | 1 - spec/lib/feature_spec.rb | 190 ++--- .../codequality_reports_comparer_spec.rb | 105 ++- .../ci/reports/codequality_reports_spec.rb | 32 + .../create_note_on_issuable_handler_spec.rb | 2 +- spec/lib/gitlab/gon_helper_spec.rb | 1 - spec/lib/gitlab/graphql/docs/renderer_spec.rb | 82 +- spec/lib/gitlab/metrics/methods_spec.rb | 5 - spec/lib/gitlab/metrics_spec.rb | 5 - .../quick_actions/command_definition_spec.rb | 8 +- .../users/in_product_marketing_email_spec.rb | 43 +- spec/requests/api/features_spec.rb | 2 - spec/requests/api/triggers_spec.rb | 33 + spec/requests/api/usage_data_spec.rb | 2 - ...n_product_marketing_emails_service_spec.rb | 72 +- spec/services/notes/create_service_spec.rb | 18 + .../quick_actions/interpret_service_spec.rb | 136 +-- .../shared_contexts/email_shared_context.rb | 10 +- .../nav_sidebar_shared_examples.rb | 10 + .../nav/sidebar/_group.html.haml_spec.rb | 1 + .../nav/sidebar/_profile.html.haml_spec.rb | 1 + .../nav/sidebar/_project.html.haml_spec.rb | 2 + 76 files changed, 1410 insertions(+), 852 deletions(-) create mode 100644 app/helpers/sidebars_helper.rb create mode 100644 changelogs/unreleased/323431_enable_allow_force_push_to_protected_branches_ff.yml create mode 100644 changelogs/unreleased/325937-fix-notes-n-plus-1.yml create mode 100644 changelogs/unreleased/ajk-gitlab-docs-deprecation-use-schema-types.yml create mode 100644 changelogs/unreleased/change-payload-format-for-dora-metrics.yml create mode 100644 changelogs/unreleased/eb-sort-code-quality-mr-widget.yml create mode 100644 changelogs/unreleased/enable-maven-query-optimization-ff-by-default.yml create mode 100644 changelogs/unreleased/jivanvl-fix-tooltip-position-mini-pipeline-chart.yml create mode 100644 changelogs/unreleased/notes_quick_action_feedback.yml create mode 100644 changelogs/unreleased/revert-0767b9e2.yml delete mode 100644 config/feature_flags/ops/prometheus_metrics_method_instrumentation.yml delete mode 100644 config/feature_flags/ops/prometheus_metrics_view_instrumentation.yml create mode 100644 spec/fixtures/emails/update_commands_only.eml create mode 100644 spec/helpers/sidebars_helper_spec.rb diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md index 4c144f06f67..16d7446fc21 100644 --- a/.gitlab/issue_templates/Feature Flag Roll Out.md +++ b/.gitlab/issue_templates/Feature Flag Roll Out.md @@ -1,49 +1,122 @@ -## What +## Feature -Remove the `:feature_name` feature flag ... +This feature uses the `:feature_name` feature flag! + + +- [Issue Name](ISSUE LINK) ## Owners - Team: NAME_OF_TEAM - Most appropriate slack channel to reach out to: `#g_TEAM_NAME` - Best individual to reach out to: NAME +- PM: NAME -## Expectations +## Stakeholders -### What are we expecting to happen? + -### What can we monitor to detect problems with this? +## The Rollout Plan - + +- [ ] Partial Rollout on GitLab.com with beta groups +- [ ] Rollout on GitLab.com for a certain period (How long) +- [ ] Percentage Rollout on GitLab.com - XX% + If it is possible to perform an incremental rollout, this should be preferred. Proposed increments are: `10%`, `50%`, `100%`. Proposed minimum time between increments is 15 minutes. +- [ ] Rollout Feature for everyone as soon as it's ready -## Beta groups/projects + -If applicable, any groups/projects that are happy to have this feature turned on early. Some organizations may wish to test big changes they are interested in with a small subset of users ahead of time for example. +**Beta Groups/Projects:** + - `gitlab-org/gitlab` project - `gitlab-org`/`gitlab-com` groups - ... -## Roll Out Steps +## Expectations + +### What are we expecting to happen? + + + +### What might happen if this goes wrong? + + + +### What can we monitor to detect problems with this? + + + +## Rollout Timeline + + + +**Initial Rollout** + +*Preperation Phase* - [ ] Enable on staging (`/chatops run feature set feature_name true --staging`) + - [ ] Test on staging -- [ ] Ensure that documentation has been updated -- [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour (`/chatops run feature set --project=gitlab-org/gitlab feature_name true`) -- [ ] If it is possible to perform an incremental rollout, this should be preferred. Proposed increments are: `10%`, `50%`, `100%`. Proposed minimum time between increments is 15 minutes. + +- [ ] Ensure that documentation has been updated ([More info](https://docs.gitlab.com/ee/development/documentation/feature_flags.html#features-that-became-enabled-by-default)) + - [ ] Coordinate a time to enable the flag with the SRE oncall and release managers - - In `#production` mention `@sre-oncall` and `@release-managers`. Once an SRE on call and Release Manager on call confirm, you can proceed with the rollout -- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com. **Note**: Once a feature rollout has started, it is not necessary to inform `@sre-oncall`/`@release-managers` at each stage of the gradual rollout. + - In `#production` by pinging `@sre-oncall` + - In `#g_delivery` by pinging `@release-managers` + +- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com + +*Partial Rollout Phase* +- [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour (`/chatops run feature set --project=gitlab-org/gitlab feature_name true`) + +- [ ] Verify behaviour (See Beta Groups) and add details with screenshots as a comment on this issue + + +**Global Availability** ([More Info](https://docs.gitlab.com/ee/development/feature_flags/controls.html#communicate-the-change)) +*(Please Note that Beta,Alpha and General Availability (GA) are handled on a product level and not the feature-flag)* + + +- [ ] Coordinate a time to enable the flag with `#production` and `#g_delivery` on slack. + +- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com + +- [ ] Make the feature flag enabled by default i.e. Change `default_enabled` to `true` + - [ ] Enable on GitLab.com by running chatops command in `#production` (`/chatops run feature set feature_name true`) -- [ ] Cross post chatops Slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel + - [ ] Announce on the issue that the flag has been enabled -- [ ] Remove feature flag and add changelog entry. Ensure that the feature flag definition YAML file has been removed in the **same MR** that is removing the feature flag from the code -- [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel + +- [ ] Cross post chatops slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel + + +**Cleanup** + +This is an __important__ phase, that should be either done in the next Milestone or as soon as possible. For the cleanup phase, please follow our documentation on how to [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up). + + +- [ ] Announce on the issue that the flag has been enabled + +- [ ] Remove `:feature_name` feature flag + - [ ] Remove all references to the feature flag from the codebase + - [ ] Remove the YAML definitions for the feature from the repository + - [ ] Create a Changelog Entry + +- [ ] Clean up the feature flag from all environments by running this chatops command in `#production` channel `/chatops run feature delete some_feature`. + +**Final Step** + +- [ ] Close this rollout issue for the feature flag after the feature flag is removed from the codebase. ## Rollback Steps @@ -54,3 +127,4 @@ If applicable, any groups/projects that are happy to have this feature turned on ``` /label ~"feature flag" +/assign DRI diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue index df83488a226..7ff9ad8da2d 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_item.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue @@ -161,7 +161,11 @@ export default { > { includes(:system_note_metadata) } + scope :with_web_entity_associations, -> { preload(:project, :author, :noteable) } scope :for_note_or_capitalized_note, ->(text) { where(note: [text, text.capitalize]) } scope :like_note_or_capitalized_note, ->(text) { where('(note LIKE ? OR note LIKE ?)', text, text.capitalize) } diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index 10c2e2e34f5..9d0eeed525c 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -142,7 +142,7 @@ class Packages::Package < ApplicationRecord end def self.only_maven_packages_with_path(path, use_cte: false) - if use_cte && Feature.enabled?(:maven_metadata_by_path_with_optimization_fence) + if use_cte && Feature.enabled?(:maven_metadata_by_path_with_optimization_fence, default_enabled: :yaml) # This is an optimization fence which assumes that looking up the Metadatum record by path (globally) # and then filter down the packages (by project or by group and subgroups) will be cheaper than # looking up all packages within a project or group and filter them by path. diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index cbbdd091feb..963a6b7774a 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -30,7 +30,7 @@ class ProtectedBranch < ApplicationRecord end def self.allow_force_push?(project, ref_name) - return false unless ::Feature.enabled?(:allow_force_push_to_protected_branches, project) + return false unless ::Feature.enabled?(:allow_force_push_to_protected_branches, project, default_enabled: :yaml) project.protected_branches.allowing_force_push.matching(ref_name).any? end diff --git a/app/models/users/in_product_marketing_email.rb b/app/models/users/in_product_marketing_email.rb index f43705dfb3d..b5a08716c7f 100644 --- a/app/models/users/in_product_marketing_email.rb +++ b/app/models/users/in_product_marketing_email.rb @@ -21,8 +21,19 @@ module Users team: 3 }, _suffix: true - scope :without_track_or_series, -> (track, series) do - where.not(track: track).or(where.not(series: series)) + scope :without_track_and_series, -> (track, series) do + users = User.arel_table + product_emails = arel_table + + join_condition = users[:id].eq(product_emails[:user_id]) + .and(product_emails[:track]).eq(tracks[track]) + .and(product_emails[:series]).eq(series) + + arel_join = users.join(product_emails, Arel::Nodes::OuterJoin).on(join_condition) + + joins(arel_join.join_sources) + .where(in_product_marketing_emails: { id: nil }) + .select(Arel.sql("DISTINCT ON(#{users.table_name}.id) #{users.table_name}.*")) end end end diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb index 19a90d002aa..a5e6ffd55b3 100644 --- a/app/presenters/search_service_presenter.rb +++ b/app/presenters/search_service_presenter.rb @@ -9,7 +9,8 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated projects: :with_web_entity_associations, issues: :with_web_entity_associations, merge_requests: :with_web_entity_associations, - epics: :with_web_entity_associations + epics: :with_web_entity_associations, + notes: :with_web_entity_associations }.freeze SORT_ENABLED_SCOPES = %w(issues merge_requests).freeze diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb index 602ae53beaf..a5f70d62e13 100644 --- a/app/services/ci/pipeline_trigger_service.rb +++ b/app/services/ci/pipeline_trigger_service.rb @@ -6,8 +6,10 @@ module Ci def execute if trigger_from_token + set_application_context_from_trigger(trigger_from_token) create_pipeline_from_trigger(trigger_from_token) elsif job_from_token + set_application_context_from_job(job_from_token) create_pipeline_from_job(job_from_token) end @@ -87,5 +89,20 @@ module Ci value: params.except(*PAYLOAD_VARIABLE_HIDDEN_PARAMS).to_json, variable_type: :file } end + + def set_application_context_from_trigger(trigger) + Gitlab::ApplicationContext.push( + user: trigger.owner, + project: trigger.project + ) + end + + def set_application_context_from_job(job) + Gitlab::ApplicationContext.push( + user: job.user, + project: job.project, + runner: job.runner + ) + end end end diff --git a/app/services/namespaces/in_product_marketing_emails_service.rb b/app/services/namespaces/in_product_marketing_emails_service.rb index 3a03baa7364..aa29c8574ad 100644 --- a/app/services/namespaces/in_product_marketing_emails_service.rb +++ b/app/services/namespaces/in_product_marketing_emails_service.rb @@ -23,7 +23,6 @@ module Namespaces def initialize(track, interval) @track = track @interval = interval - @current_batch_user_ids = [] @in_product_marketing_email_records = [] end @@ -35,13 +34,11 @@ module Namespaces send_email_for_group(group) end end - - record_sent_emails end private - attr_reader :track, :interval, :current_batch_user_ids, :in_product_marketing_email_records + attr_reader :track, :interval, :in_product_marketing_email_records def send_email_for_group(group) experiment_enabled_for_group = experiment_enabled_for_group?(group) @@ -49,8 +46,13 @@ module Namespaces return unless experiment_enabled_for_group users_for_group(group).each do |user| - send_email(user, group) if can_perform_action?(user, group) + if can_perform_action?(user, group) + send_email(user, group) + track_sent_email(user, track, series) + end end + + save_tracked_emails! end def experiment_enabled_for_group?(group) @@ -75,13 +77,9 @@ module Namespaces end def users_for_group(group) - group.users.where(email_opted_in: true) - .where.not(id: current_batch_user_ids) - .left_outer_joins(:in_product_marketing_emails) - .merge( - Users::InProductMarketingEmail.without_track_or_series(track, series) - .or(Users::InProductMarketingEmail.where(id: nil)) - ) + group.users + .where(email_opted_in: true) + .merge(Users::InProductMarketingEmail.without_track_and_series(track, series)) end # rubocop: enable CodeReuse/ActiveRecord @@ -100,8 +98,6 @@ module Namespaces def send_email(user, group) NotificationService.new.in_product_marketing(user.id, group.id, track, series) - - track_sent_email(user, group, track, series) end def completed_actions @@ -122,13 +118,12 @@ module Namespaces INTERVAL_DAYS.index(interval) end - def record_sent_emails + def save_tracked_emails! Users::InProductMarketingEmail.bulk_insert!(in_product_marketing_email_records) + @in_product_marketing_email_records = [] end - def track_sent_email(user, group, track, series) - current_batch_user_ids << user.id - + def track_sent_email(user, track, series) in_product_marketing_email_records << Users::InProductMarketingEmail.new( user: user, track: track, diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index d47df24b1b9..ae557b73620 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,11 +1,11 @@ - @hide_top_links = true -- page_title _("Merge Requests") +- page_title _("Merge requests") - @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username) = render_dashboard_ultimate_trial(current_user) .page-title-holder.d-flex.align-items-start.flex-column.flex-sm-row.align-items-sm-center - %h1.page-title= _('Merge Requests') + %h1.page-title= _('Merge requests') - if current_user .page-title-controls.ml-0.mb-3.ml-sm-auto.mb-sm-0 diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index e2a701a9fc0..e9437ab8a3f 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -3,7 +3,7 @@ - aside_title = @group.subgroup? ? _('Subgroup navigation') : _('Group navigation') - overview_title = @group.subgroup? ? _('Subgroup overview') : _('Group overview') -%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('groups_side_navigation', 'render', 'groups_side_navigation'), 'aria-label': aside_title } +%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(@group), 'aria-label': aside_title } .nav-sidebar-inner-scroll .context-header = link_to group_path(@group), title: @group.name do diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 4ae81d69c16..dda5e6b9636 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -1,4 +1,4 @@ -%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('user_side_navigation', 'render', 'user_side_navigation'), 'aria-label': _('User settings') } +%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(current_user), 'aria-label': _('User settings') } .nav-sidebar-inner-scroll .context-header = link_to profile_path, title: _('Profile Settings') do diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index b172ba67ef1..6b9cf644044 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -1,4 +1,4 @@ -%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation'), 'aria-label': _('Project navigation') } +%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(@project), 'aria-label': _('Project navigation') } .nav-sidebar-inner-scroll .context-header = link_to project_path(@project), title: @project.name do diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml index 522e9888bc6..2691513c994 100644 --- a/app/views/projects/protected_branches/shared/_branches_list.html.haml +++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml @@ -23,7 +23,7 @@ %th = s_("ProtectedBranch|Allowed to push") - - if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project) + - if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project, default_enabled: :yaml) %th = s_("ProtectedBranch|Allow force push") %span.has-tooltip{ data: { container: 'body' }, title: s_('ProtectedBranch|Allow force push for all users with push access.'), 'aria-hidden': 'true' } diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml index 308fe90d608..9fdcea96c00 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -21,7 +21,7 @@ = f.label :push_access_levels_attributes, s_("ProtectedBranch|Allowed to push:"), class: 'col-md-2 text-left text-md-right' .col-md-10 = yield :push_access_levels - - if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project) + - if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project, default_enabled: :yaml) .form-group.row = f.label :allow_force_push, s_("ProtectedBranch|Allow force push:"), class: 'col-md-2 gl-text-left text-md-right' .col-md-10 diff --git a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml index 75d6d88fbc3..6a362866f41 100644 --- a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml +++ b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml @@ -33,6 +33,6 @@ %p.small = _('Members of %{group} can also push to this branch: %{branch}') % { group: (group_push_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_push_access_levels.map(&:humanize).to_sentence } -- if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project) +- if ::Feature.enabled?(:allow_force_push_to_protected_branches, @project, default_enabled: :yaml) %td = render "shared/buttons/project_feature_toggle", is_checked: protected_branch.allow_force_push, label: s_("ProtectedBranch|Toggle allow force push"), class_list: "js-force-push-toggle project-feature-toggle", data: { qa_selector: 'force_push_toggle_button', qa_branch_name: protected_branch.name } diff --git a/changelogs/unreleased/323431_enable_allow_force_push_to_protected_branches_ff.yml b/changelogs/unreleased/323431_enable_allow_force_push_to_protected_branches_ff.yml new file mode 100644 index 00000000000..dcd806936e7 --- /dev/null +++ b/changelogs/unreleased/323431_enable_allow_force_push_to_protected_branches_ff.yml @@ -0,0 +1,5 @@ +--- +title: Allow users to enable force push to protected branches +merge_request: 57053 +author: +type: added diff --git a/changelogs/unreleased/325937-fix-notes-n-plus-1.yml b/changelogs/unreleased/325937-fix-notes-n-plus-1.yml new file mode 100644 index 00000000000..a5cb14ade71 --- /dev/null +++ b/changelogs/unreleased/325937-fix-notes-n-plus-1.yml @@ -0,0 +1,5 @@ +--- +title: Fix N+1 for searching notes (comments) scope +merge_request: 57460 +author: +type: performance diff --git a/changelogs/unreleased/ajk-gitlab-docs-deprecation-use-schema-types.yml b/changelogs/unreleased/ajk-gitlab-docs-deprecation-use-schema-types.yml new file mode 100644 index 00000000000..2f1eb48753a --- /dev/null +++ b/changelogs/unreleased/ajk-gitlab-docs-deprecation-use-schema-types.yml @@ -0,0 +1,5 @@ +--- +title: Change the way deprecation information is presented in GraphQL documentation +merge_request: 56864 +author: +type: changed diff --git a/changelogs/unreleased/change-payload-format-for-dora-metrics.yml b/changelogs/unreleased/change-payload-format-for-dora-metrics.yml new file mode 100644 index 00000000000..4a8d1db3457 --- /dev/null +++ b/changelogs/unreleased/change-payload-format-for-dora-metrics.yml @@ -0,0 +1,5 @@ +--- +title: Improve payload format of DORA metrics API +merge_request: 57314 +author: +type: added diff --git a/changelogs/unreleased/eb-sort-code-quality-mr-widget.yml b/changelogs/unreleased/eb-sort-code-quality-mr-widget.yml new file mode 100644 index 00000000000..709dd3e1fa3 --- /dev/null +++ b/changelogs/unreleased/eb-sort-code-quality-mr-widget.yml @@ -0,0 +1,5 @@ +--- +title: Sort code quality degradations in MR Widget comparison reports +merge_request: 57258 +author: +type: added diff --git a/changelogs/unreleased/enable-maven-query-optimization-ff-by-default.yml b/changelogs/unreleased/enable-maven-query-optimization-ff-by-default.yml new file mode 100644 index 00000000000..e9c60a3b79b --- /dev/null +++ b/changelogs/unreleased/enable-maven-query-optimization-ff-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Optimize group level Maven package finder query +merge_request: 57692 +author: +type: performance diff --git a/changelogs/unreleased/jivanvl-fix-tooltip-position-mini-pipeline-chart.yml b/changelogs/unreleased/jivanvl-fix-tooltip-position-mini-pipeline-chart.yml new file mode 100644 index 00000000000..7ebb430b343 --- /dev/null +++ b/changelogs/unreleased/jivanvl-fix-tooltip-position-mini-pipeline-chart.yml @@ -0,0 +1,5 @@ +--- +title: Fix tooltip position in mini pipeline chart +merge_request: 57425 +author: +type: fixed diff --git a/changelogs/unreleased/notes_quick_action_feedback.yml b/changelogs/unreleased/notes_quick_action_feedback.yml new file mode 100644 index 00000000000..4e7700b84c4 --- /dev/null +++ b/changelogs/unreleased/notes_quick_action_feedback.yml @@ -0,0 +1,5 @@ +--- +title: Give better feedback when quick actions have no effect +merge_request: 57570 +author: Hilco van der Wilk +type: fixed diff --git a/changelogs/unreleased/revert-0767b9e2.yml b/changelogs/unreleased/revert-0767b9e2.yml new file mode 100644 index 00000000000..e06cf2926b6 --- /dev/null +++ b/changelogs/unreleased/revert-0767b9e2.yml @@ -0,0 +1,5 @@ +--- +title: Revert Ignore default_enabled value in Feature.enabled? +merge_request: 57707 +author: +type: fixed diff --git a/config/feature_flags/development/allow_force_push_to_protected_branches.yml b/config/feature_flags/development/allow_force_push_to_protected_branches.yml index 987c7d4bb7b..632e2ad0ccd 100644 --- a/config/feature_flags/development/allow_force_push_to_protected_branches.yml +++ b/config/feature_flags/development/allow_force_push_to_protected_branches.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323431 milestone: '13.10' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/maven_metadata_by_path_with_optimization_fence.yml b/config/feature_flags/development/maven_metadata_by_path_with_optimization_fence.yml index 683083e3dae..7b55cde4998 100644 --- a/config/feature_flags/development/maven_metadata_by_path_with_optimization_fence.yml +++ b/config/feature_flags/development/maven_metadata_by_path_with_optimization_fence.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/325460 milestone: '13.11' type: development group: group::optimize -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/ops/prometheus_metrics_method_instrumentation.yml b/config/feature_flags/ops/prometheus_metrics_method_instrumentation.yml deleted file mode 100644 index d5375a98ea0..00000000000 --- a/config/feature_flags/ops/prometheus_metrics_method_instrumentation.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: prometheus_metrics_method_instrumentation -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4304 -rollout_issue_url: -milestone: '10.5' -type: ops -group: -default_enabled: false diff --git a/config/feature_flags/ops/prometheus_metrics_view_instrumentation.yml b/config/feature_flags/ops/prometheus_metrics_view_instrumentation.yml deleted file mode 100644 index ebc31ede176..00000000000 --- a/config/feature_flags/ops/prometheus_metrics_view_instrumentation.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: prometheus_metrics_view_instrumentation -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4304 -rollout_issue_url: -milestone: '10.5' -type: ops -group: -default_enabled: false diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md index e04e1fe27b4..b887294b28e 100644 --- a/doc/api/dora/metrics.md +++ b/doc/api/dora/metrics.md @@ -38,14 +38,14 @@ Example response: ```json [ - { "2021-03-01": 3 }, - { "2021-03-02": 6 }, - { "2021-03-03": 0 }, - { "2021-03-04": 0 }, - { "2021-03-05": 0 }, - { "2021-03-06": 0 }, - { "2021-03-07": 0 }, - { "2021-03-08": 4 } + { "2021-03-01": 3, "date": "2021-03-01", "value": 3 }, + { "2021-03-02": 6, "date": "2021-03-02", "value": 6 }, + { "2021-03-03": 0, "date": "2021-03-03", "value": 0 }, + { "2021-03-04": 0, "date": "2021-03-04", "value": 0 }, + { "2021-03-05": 0, "date": "2021-03-05", "value": 0 }, + { "2021-03-06": 0, "date": "2021-03-06", "value": 0 }, + { "2021-03-07": 0, "date": "2021-03-07", "value": 0 }, + { "2021-03-08": 4, "date": "2021-03-08", "value": 4 } ] ``` @@ -78,13 +78,13 @@ Example response: ```json [ - { "2021-03-01": 3 }, - { "2021-03-02": 6 }, - { "2021-03-03": 0 }, - { "2021-03-04": 0 }, - { "2021-03-05": 0 }, - { "2021-03-06": 0 }, - { "2021-03-07": 0 }, - { "2021-03-08": 4 } + { "2021-03-01": 3, "date": "2021-03-01", "value": 3 }, + { "2021-03-02": 6, "date": "2021-03-02", "value": 6 }, + { "2021-03-03": 0, "date": "2021-03-03", "value": 0 }, + { "2021-03-04": 0, "date": "2021-03-04", "value": 0 }, + { "2021-03-05": 0, "date": "2021-03-05", "value": 0 }, + { "2021-03-06": 0, "date": "2021-03-06", "value": 0 }, + { "2021-03-07": 0, "date": "2021-03-07", "value": 0 }, + { "2021-03-08": 4, "date": "2021-03-08", "value": 4 } ] ``` diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d6bebfb5280..611520b4e19 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -135,7 +135,12 @@ Returns [`InstanceSecurityDashboard`](#instancesecuritydashboard). ### `instanceStatisticsMeasurements` -Get statistics on the instance. Deprecated in 13.10: This field was renamed. Use the `usageTrendsMeasurements` field instead. +Get statistics on the instance. + +WARNING: +**Deprecated** in 13.10. +This was renamed. +Use: `Query.usageTrendsMeasurements`. Returns [`UsageTrendsMeasurementConnection`](#usagetrendsmeasurementconnection). @@ -393,7 +398,12 @@ Returns [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnec ### `vulnerabilitiesCountByDayAndSeverity` -Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`. +Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard. + +WARNING: +**Deprecated** in 13.3. +Use of this is not recommended. +Use: `Query.vulnerabilitiesCountByDay`. Returns [`VulnerabilitiesCountByDayAndSeverityConnection`](#vulnerabilitiescountbydayandseverityconnection). @@ -488,7 +498,7 @@ Describes an alert from the project's Alert Management. | `hosts` | [`[String!]`](#string) | List of hosts the alert came from. | | `iid` | [`ID!`](#id) | Internal ID of the alert. | | `issue` | [`Issue`](#issue) | Issue attached to the alert. | -| `issueIid` **{warning-solid}** | [`ID`](#id) | **Deprecated:** Use issue field. Deprecated in 13.10. | +| `issueIid` **{warning-solid}** | [`ID`](#id) | **Deprecated** in 13.10. Use issue field. | | `metricsDashboardUrl` | [`String`](#string) | URL for metrics embed for the alert. | | `monitoringTool` | [`String`](#string) | Monitoring tool the alert came from. | | `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. | @@ -527,12 +537,12 @@ Represents total number of alerts for the represented categories. | Field | Type | Description | | ----- | ---- | ----------- | -| `acknowledged` | [`Int`](#int) | Number of alerts with status ACKNOWLEDGED for the project | +| `acknowledged` | [`Int`](#int) | Number of alerts with status ACKNOWLEDGED for the project. | | `all` | [`Int`](#int) | Total number of alerts for the project. | -| `ignored` | [`Int`](#int) | Number of alerts with status IGNORED for the project | +| `ignored` | [`Int`](#int) | Number of alerts with status IGNORED for the project. | | `open` | [`Int`](#int) | Number of alerts with status TRIGGERED or ACKNOWLEDGED for the project. | -| `resolved` | [`Int`](#int) | Number of alerts with status RESOLVED for the project | -| `triggered` | [`Int`](#int) | Number of alerts with status TRIGGERED for the project | +| `resolved` | [`Int`](#int) | Number of alerts with status RESOLVED for the project. | +| `triggered` | [`Int`](#int) | Number of alerts with status TRIGGERED for the project. | ### `AlertManagementHttpIntegration` @@ -870,7 +880,7 @@ Represents an epic on an issue board. | `upvotes` | [`Int!`](#int) | Number of upvotes the epic has received. | | `userDiscussionsCount` | [`Int!`](#int) | Number of user discussions in the epic. | | `userNotesCount` | [`Int!`](#int) | Number of user notes of the epic. | -| `userPermissions` | [`EpicPermissions!`](#epicpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`EpicPermissions!`](#epicpermissions) | Permissions for the current user on the resource. | | `userPreferences` | [`BoardEpicUserPreferences`](#boardepicuserpreferences) | User preferences for the epic on the issue board. | | `webPath` | [`String!`](#string) | Web path of the epic. | | `webUrl` | [`String!`](#string) | Web URL of the epic. | @@ -1428,7 +1438,7 @@ Represents the code coverage summary for a project. | `authorName` | [`String`](#string) | Commit authors name. | | `authoredDate` | [`Time`](#time) | Timestamp of when the commit was authored. | | `description` | [`String`](#string) | Description of the commit message. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `id` | [`ID!`](#id) | ID (global ID) of the commit. | | `message` | [`String`](#string) | Raw commit message. | | `pipelines` | [`PipelineConnection`](#pipelineconnection) | Pipelines of the commit ordered latest first. | @@ -1436,7 +1446,7 @@ Represents the code coverage summary for a project. | `shortId` | [`String!`](#string) | Short SHA1 ID of the commit. | | `signatureHtml` | [`String`](#string) | Rendered HTML of the commit signature. | | `title` | [`String`](#string) | Title of the commit message. | -| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title` | +| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | `webPath` | [`String!`](#string) | Web path of the commit. | | `webUrl` | [`String!`](#string) | Web URL of the commit. | @@ -1935,7 +1945,7 @@ Represents a DAST scanner profile. | Field | Type | Description | | ----- | ---- | ----------- | | `editPath` | [`String`](#string) | Relative web path to the edit page of a scanner profile. | -| `globalId` **{warning-solid}** | [`DastScannerProfileID!`](#dastscannerprofileid) | **Deprecated:** Use `id`. Deprecated in 13.6. | +| `globalId` **{warning-solid}** | [`DastScannerProfileID!`](#dastscannerprofileid) | **Deprecated** in 13.6. Use `id`. | | `id` | [`DastScannerProfileID!`](#dastscannerprofileid) | ID of the DAST scanner profile. | | `profileName` | [`String`](#string) | Name of the DAST scanner profile. | | `referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. | @@ -1963,7 +1973,7 @@ Autogenerated return type of DastScannerProfileCreate. | ----- | ---- | ----------- | | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | -| `globalId` **{warning-solid}** | [`DastScannerProfileID`](#dastscannerprofileid) | **Deprecated:** Use `id`. Deprecated in 13.6. | +| `globalId` **{warning-solid}** | [`DastScannerProfileID`](#dastscannerprofileid) | **Deprecated** in 13.6. Use `id`. | | `id` | [`DastScannerProfileID`](#dastscannerprofileid) | ID of the scanner profile. | ### `DastScannerProfileDeletePayload` @@ -2009,7 +2019,7 @@ Represents a DAST Site Profile. | `referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. | | `requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. Will always return `null` if `security_dast_site_profiles_additional_fields` feature flag is disabled. | | `targetUrl` | [`String`](#string) | The URL of the target to be scanned. | -| `userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource. | | `validationStatus` | [`DastSiteProfileValidationStatusEnum`](#dastsiteprofilevalidationstatusenum) | The current validation status of the site profile. | ### `DastSiteProfileAuth` @@ -2069,7 +2079,7 @@ Check permissions for the current user on site profile. | Field | Type | Description | | ----- | ---- | ----------- | -| `createOnDemandDastScan` | [`Boolean!`](#boolean) | Indicates the user can perform `create_on_demand_dast_scan` on this resource | +| `createOnDemandDastScan` | [`Boolean!`](#boolean) | Indicates the user can perform `create_on_demand_dast_scan` on this resource. | ### `DastSiteProfileUpdatePayload` @@ -2184,7 +2194,7 @@ A single design. | `fullPath` | [`String!`](#string) | The full path to the design file. | | `id` | [`ID!`](#id) | The ID of this design. | | `image` | [`String!`](#string) | The URL of the full-sized image. | -| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated | +| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated. | | `issue` | [`Issue!`](#issue) | The issue the design belongs to. | | `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. | | `notesCount` | [`Int!`](#int) | The total count of user-created notes for this design. | @@ -2204,7 +2214,7 @@ A design pinned to a specific version. The image field reflects the design as of | `fullPath` | [`String!`](#string) | The full path to the design file. | | `id` | [`ID!`](#id) | The ID of this design. | | `image` | [`String!`](#string) | The URL of the full-sized image. | -| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated | +| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated. | | `issue` | [`Issue!`](#issue) | The issue the design belongs to. | | `notesCount` | [`Int!`](#int) | The total count of user-created notes for this design. | | `project` | [`Project!`](#project) | The project the design belongs to. | @@ -2299,7 +2309,7 @@ Autogenerated return type of DesignManagementUpload. | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `designs` | [`[Design!]!`](#design) | The designs that were uploaded by the mutation. | | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | -| `skippedDesigns` | [`[Design!]!`](#design) | Any designs that were skipped from the upload due to there being no change to their content since their last version | +| `skippedDesigns` | [`[Design!]!`](#design) | Any designs that were skipped from the upload due to there being no change to their content since their last version. | ### `DesignVersion` @@ -2658,7 +2668,7 @@ Represents an epic. | `upvotes` | [`Int!`](#int) | Number of upvotes the epic has received. | | `userDiscussionsCount` | [`Int!`](#int) | Number of user discussions in the epic. | | `userNotesCount` | [`Int!`](#int) | Number of user notes of the epic. | -| `userPermissions` | [`EpicPermissions!`](#epicpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`EpicPermissions!`](#epicpermissions) | Permissions for the current user on the resource. | | `webPath` | [`String!`](#string) | Web path of the epic. | | `webUrl` | [`String!`](#string) | Web URL of the epic. | @@ -2804,7 +2814,7 @@ Relationship between an epic and an issue. | `createdAt` | [`Time!`](#time) | Timestamp of when the issue was created. | | `currentUserTodos` | [`TodoConnection!`](#todoconnection) | To-do items for the current user. | | `description` | [`String`](#string) | Description of the issue. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `designCollection` | [`DesignCollection`](#designcollection) | Collection of design images associated with this issue. | | `discussionLocked` | [`Boolean!`](#boolean) | Indicates discussion is locked on the issue. | | `discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on this noteable. | @@ -2837,7 +2847,7 @@ Relationship between an epic and an issue. | `taskCompletionStatus` | [`TaskCompletionStatus!`](#taskcompletionstatus) | Task completion status of the issue. | | `timeEstimate` | [`Int!`](#int) | Time estimate of the issue. | | `title` | [`String!`](#string) | Title of the issue. | -| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title` | +| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | `totalTimeSpent` | [`Int!`](#int) | Total time reported as spent on the issue. | | `type` | [`IssueType`](#issuetype) | Type of the issue. | | `updatedAt` | [`Time!`](#time) | Timestamp of when the issue was last updated. | @@ -2845,7 +2855,7 @@ Relationship between an epic and an issue. | `upvotes` | [`Int!`](#int) | Number of upvotes the issue has received. | | `userDiscussionsCount` | [`Int!`](#int) | Number of user discussions in the issue. | | `userNotesCount` | [`Int!`](#int) | Number of user notes of the issue. | -| `userPermissions` | [`IssuePermissions!`](#issuepermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`IssuePermissions!`](#issuepermissions) | Permissions for the current user on the resource. | | `webPath` | [`String!`](#string) | Web path of the issue. | | `webUrl` | [`String!`](#string) | Web URL of the issue. | | `weight` | [`Int`](#int) | Weight of the issue. | @@ -2920,14 +2930,14 @@ Check permissions for the current user on an epic. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_epic` on this resource | -| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource | -| `createEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `create_epic` on this resource | -| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource | -| `destroyEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_epic` on this resource | -| `readEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `read_epic` on this resource | -| `readEpicIid` | [`Boolean!`](#boolean) | Indicates the user can perform `read_epic_iid` on this resource | -| `updateEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `update_epic` on this resource | +| `adminEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_epic` on this resource. | +| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource. | +| `createEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `create_epic` on this resource. | +| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource. | +| `destroyEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_epic` on this resource. | +| `readEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `read_epic` on this resource. | +| `readEpicIid` | [`Boolean!`](#boolean) | Indicates the user can perform `read_epic_iid` on this resource. | +| `updateEpic` | [`Boolean!`](#boolean) | Indicates the user can perform `update_epic` on this resource. | ### `EpicSetSubscriptionPayload` @@ -3063,13 +3073,13 @@ Autogenerated return type of GitlabSubscriptionActivate. | `containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. | | `customEmoji` | [`CustomEmojiConnection`](#customemojiconnection) | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled. | | `description` | [`String`](#string) | Description of the namespace. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. | | `epic` | [`Epic`](#epic) | Find a single epic. | | `epicBoard` | [`EpicBoard`](#epicboard) | Find a single epic board. | | `epicBoards` | [`EpicBoardConnection`](#epicboardconnection) | Find epic boards. | | `epics` | [`EpicConnection`](#epicconnection) | Find epics. | -| `epicsEnabled` | [`Boolean`](#boolean) | Indicates if Epics are enabled for namespace | +| `epicsEnabled` | [`Boolean`](#boolean) | Indicates if Epics are enabled for namespace. | | `fullName` | [`String!`](#string) | Full name of the namespace. | | `fullPath` | [`ID!`](#id) | Full path of the namespace. | | `groupMembers` | [`GroupMemberConnection`](#groupmemberconnection) | A membership of a user within this group. | @@ -3104,11 +3114,11 @@ Autogenerated return type of GitlabSubscriptionActivate. | `totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. | | `totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. | | `twoFactorGracePeriod` | [`Int`](#int) | Time before two-factor authentication is enforced. | -| `userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. | | `visibility` | [`String`](#string) | Visibility of the namespace. | | `vulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Vulnerabilities reported on the projects in the group and its subgroups. | | `vulnerabilitiesCountByDay` | [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnection) | Number of vulnerabilities per day for the projects in the group and its subgroups. | -| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | [`VulnerabilitiesCountByDayAndSeverityConnection`](#vulnerabilitiescountbydayandseverityconnection) | **Deprecated:** Use `vulnerabilitiesCountByDay`. Deprecated in 13.3. | +| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | [`VulnerabilitiesCountByDayAndSeverityConnection`](#vulnerabilitiescountbydayandseverityconnection) | **Deprecated** in 13.3. Use `vulnerabilitiesCountByDay`. | | `vulnerabilityGrades` | [`[VulnerableProjectsByGrade!]!`](#vulnerableprojectsbygrade) | Represents vulnerable project counts for each grade. | | `vulnerabilityScanners` | [`VulnerabilityScannerConnection`](#vulnerabilityscannerconnection) | Vulnerability scanners reported on the project vulnerabilities of the group and its subgroups. | | `vulnerabilitySeveritiesCount` | [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount) | Counts for each vulnerability severity in the group and its subgroups. | @@ -3128,7 +3138,7 @@ Represents a Group Membership. | `id` | [`ID!`](#id) | ID of the member. | | `updatedAt` | [`Time`](#time) | Date and time the membership was last updated. | | `user` | [`User!`](#user) | User that is associated with the member object. | -| `userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. | ### `GroupMemberConnection` @@ -3153,7 +3163,7 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `readGroup` | [`Boolean!`](#boolean) | Indicates the user can perform `read_group` on this resource | +| `readGroup` | [`Boolean!`](#boolean) | Indicates the user can perform `read_group` on this resource. | ### `GroupReleaseStats` @@ -3161,8 +3171,8 @@ Contains release-related statistics about a group. | Field | Type | Description | | ----- | ---- | ----------- | -| `releasesCount` | [`Int`](#int) | Total number of releases in all descendant projects of the group. Will always return `null` if `group_level_release_statistics` feature flag is disabled | -| `releasesPercentage` | [`Int`](#int) | Percentage of the group's descendant projects that have at least one release. Will always return `null` if `group_level_release_statistics` feature flag is disabled | +| `releasesCount` | [`Int`](#int) | Total number of releases in all descendant projects of the group. Will always return `null` if `group_level_release_statistics` feature flag is disabled. | +| `releasesPercentage` | [`Int`](#int) | Percentage of the group's descendant projects that have at least one release. Will always return `null` if `group_level_release_statistics` feature flag is disabled. | ### `GroupStats` @@ -3178,14 +3188,14 @@ Represents the Geo sync and verification state of a group wiki repository. | Field | Type | Description | | ----- | ---- | ----------- | -| `createdAt` | [`Time`](#time) | Timestamp when the GroupWikiRepositoryRegistry was created | +| `createdAt` | [`Time`](#time) | Timestamp when the GroupWikiRepositoryRegistry was created. | | `groupWikiRepositoryId` | [`ID!`](#id) | ID of the Group Wiki Repository. | -| `id` | [`ID!`](#id) | ID of the GroupWikiRepositoryRegistry | -| `lastSyncFailure` | [`String`](#string) | Error message during sync of the GroupWikiRepositoryRegistry | -| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the GroupWikiRepositoryRegistry | -| `retryAt` | [`Time`](#time) | Timestamp after which the GroupWikiRepositoryRegistry should be resynced | -| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the GroupWikiRepositoryRegistry | -| `state` | [`RegistryState`](#registrystate) | Sync state of the GroupWikiRepositoryRegistry | +| `id` | [`ID!`](#id) | ID of the GroupWikiRepositoryRegistry. | +| `lastSyncFailure` | [`String`](#string) | Error message during sync of the GroupWikiRepositoryRegistry. | +| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the GroupWikiRepositoryRegistry. | +| `retryAt` | [`Time`](#time) | Timestamp after which the GroupWikiRepositoryRegistry should be resynced. | +| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the GroupWikiRepositoryRegistry. | +| `state` | [`RegistryState`](#registrystate) | Sync state of the GroupWikiRepositoryRegistry. | ### `GroupWikiRepositoryRegistryConnection` @@ -3366,7 +3376,7 @@ An edge in a connection. | `createdAt` | [`Time!`](#time) | Timestamp of when the issue was created. | | `currentUserTodos` | [`TodoConnection!`](#todoconnection) | To-do items for the current user. | | `description` | [`String`](#string) | Description of the issue. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `designCollection` | [`DesignCollection`](#designcollection) | Collection of design images associated with this issue. | | `discussionLocked` | [`Boolean!`](#boolean) | Indicates discussion is locked on the issue. | | `discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on this noteable. | @@ -3397,7 +3407,7 @@ An edge in a connection. | `taskCompletionStatus` | [`TaskCompletionStatus!`](#taskcompletionstatus) | Task completion status of the issue. | | `timeEstimate` | [`Int!`](#int) | Time estimate of the issue. | | `title` | [`String!`](#string) | Title of the issue. | -| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title` | +| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | `totalTimeSpent` | [`Int!`](#int) | Total time reported as spent on the issue. | | `type` | [`IssueType`](#issuetype) | Type of the issue. | | `updatedAt` | [`Time!`](#time) | Timestamp of when the issue was last updated. | @@ -3405,7 +3415,7 @@ An edge in a connection. | `upvotes` | [`Int!`](#int) | Number of upvotes the issue has received. | | `userDiscussionsCount` | [`Int!`](#int) | Number of user discussions in the issue. | | `userNotesCount` | [`Int!`](#int) | Number of user notes of the issue. | -| `userPermissions` | [`IssuePermissions!`](#issuepermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`IssuePermissions!`](#issuepermissions) | Permissions for the current user on the resource. | | `webPath` | [`String!`](#string) | Web path of the issue. | | `webUrl` | [`String!`](#string) | Web URL of the issue. | | `weight` | [`Int`](#int) | Weight of the issue. | @@ -3457,14 +3467,14 @@ Check permissions for the current user on a issue. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_issue` on this resource | -| `createDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `create_design` on this resource | -| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource | -| `destroyDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_design` on this resource | -| `readDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `read_design` on this resource | -| `readIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `read_issue` on this resource | -| `reopenIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `reopen_issue` on this resource | -| `updateIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `update_issue` on this resource | +| `adminIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_issue` on this resource. | +| `createDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `create_design` on this resource. | +| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource. | +| `destroyDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_design` on this resource. | +| `readDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `read_design` on this resource. | +| `readIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `read_issue` on this resource. | +| `reopenIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `reopen_issue` on this resource. | +| `updateIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `update_issue` on this resource. | ### `IssueSetAssigneesPayload` @@ -3562,9 +3572,9 @@ Represents total number of issues for the represented statuses. | Field | Type | Description | | ----- | ---- | ----------- | -| `all` | [`Int`](#int) | Number of issues with status ALL for the project | -| `closed` | [`Int`](#int) | Number of issues with status CLOSED for the project | -| `opened` | [`Int`](#int) | Number of issues with status OPENED for the project | +| `all` | [`Int`](#int) | Number of issues with status ALL for the project. | +| `closed` | [`Int`](#int) | Number of issues with status CLOSED for the project. | +| `opened` | [`Int`](#int) | Number of issues with status OPENED for the project. | ### `Iteration` @@ -3574,7 +3584,7 @@ Represents an iteration object. | ----- | ---- | ----------- | | `createdAt` | [`Time!`](#time) | Timestamp of iteration creation. | | `description` | [`String`](#string) | Description of the iteration. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `dueDate` | [`Time`](#time) | Timestamp of the iteration due date. | | `id` | [`ID!`](#id) | ID of the iteration. | | `iid` | [`ID!`](#id) | Internal ID of the iteration. | @@ -3764,7 +3774,7 @@ An edge in a connection. | `color` | [`String!`](#string) | Background color of the label. | | `createdAt` | [`Time!`](#time) | When this label was created. | | `description` | [`String`](#string) | Description of the label (Markdown rendered as HTML for caching). | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `id` | [`ID!`](#id) | Label ID. | | `textColor` | [`String!`](#string) | Text color of the label. | | `title` | [`String!`](#string) | Content of the label. | @@ -3852,7 +3862,7 @@ An edge in a connection. | `defaultMergeCommitMessageWithDescription` | [`String`](#string) | Default merge commit message of the merge request with description. | | `defaultSquashCommitMessage` | [`String`](#string) | Default squash commit message of the merge request. | | `description` | [`String`](#string) | Description of the merge request (Markdown rendered as HTML for caching). | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `diffHeadSha` | [`String`](#string) | Diff head SHA of the merge request. | | `diffRefs` | [`DiffRefs`](#diffrefs) | References of the base SHA, the head SHA, and the start SHA for this merge request. | | `diffStats` | [`[DiffStats!]`](#diffstats) | Details about which files were changed in this merge request. | @@ -3906,16 +3916,16 @@ An edge in a connection. | `targetBranchExists` | [`Boolean!`](#boolean) | Indicates if the target branch of the merge request exists. | | `targetProject` | [`Project!`](#project) | Target project of the merge request. | | `targetProjectId` | [`Int!`](#int) | ID of the merge request target project. | -| `taskCompletionStatus` | [`TaskCompletionStatus!`](#taskcompletionstatus) | Completion status of tasks | +| `taskCompletionStatus` | [`TaskCompletionStatus!`](#taskcompletionstatus) | Completion status of tasks. | | `timeEstimate` | [`Int!`](#int) | Time estimate of the merge request. | | `title` | [`String!`](#string) | Title of the merge request. | -| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title` | +| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | `totalTimeSpent` | [`Int!`](#int) | Total time reported as spent on the merge request. | | `updatedAt` | [`Time!`](#time) | Timestamp of when the merge request was last updated. | | `upvotes` | [`Int!`](#int) | Number of upvotes for the merge request. | | `userDiscussionsCount` | [`Int`](#int) | Number of user discussions in the merge request. | | `userNotesCount` | [`Int`](#int) | User notes count of the merge request. | -| `userPermissions` | [`MergeRequestPermissions!`](#mergerequestpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`MergeRequestPermissions!`](#mergerequestpermissions) | Permissions for the current user on the resource. | | `webUrl` | [`String`](#string) | Web URL of the merge request. | | `workInProgress` | [`Boolean!`](#boolean) | Indicates if the merge request is a draft. | @@ -3957,14 +3967,14 @@ Represents the Geo sync and verification state of a Merge Request diff. | Field | Type | Description | | ----- | ---- | ----------- | -| `createdAt` | [`Time`](#time) | Timestamp when the MergeRequestDiffRegistry was created | -| `id` | [`ID!`](#id) | ID of the MergeRequestDiffRegistry | -| `lastSyncFailure` | [`String`](#string) | Error message during sync of the MergeRequestDiffRegistry | -| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the MergeRequestDiffRegistry | +| `createdAt` | [`Time`](#time) | Timestamp when the MergeRequestDiffRegistry was created. | +| `id` | [`ID!`](#id) | ID of the MergeRequestDiffRegistry. | +| `lastSyncFailure` | [`String`](#string) | Error message during sync of the MergeRequestDiffRegistry. | +| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the MergeRequestDiffRegistry. | | `mergeRequestDiffId` | [`ID!`](#id) | ID of the Merge Request diff. | -| `retryAt` | [`Time`](#time) | Timestamp after which the MergeRequestDiffRegistry should be resynced | -| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the MergeRequestDiffRegistry | -| `state` | [`RegistryState`](#registrystate) | Sync state of the MergeRequestDiffRegistry | +| `retryAt` | [`Time`](#time) | Timestamp after which the MergeRequestDiffRegistry should be resynced. | +| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the MergeRequestDiffRegistry. | +| `state` | [`RegistryState`](#registrystate) | Sync state of the MergeRequestDiffRegistry. | ### `MergeRequestDiffRegistryConnection` @@ -4000,15 +4010,15 @@ Check permissions for the current user on a merge request. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_merge_request` on this resource | -| `canMerge` | [`Boolean!`](#boolean) | Indicates the user can perform `can_merge` on this resource | -| `cherryPickOnCurrentMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `cherry_pick_on_current_merge_request` on this resource | -| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource | -| `pushToSourceBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `push_to_source_branch` on this resource | -| `readMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `read_merge_request` on this resource | -| `removeSourceBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_source_branch` on this resource | -| `revertOnCurrentMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `revert_on_current_merge_request` on this resource | -| `updateMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `update_merge_request` on this resource | +| `adminMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_merge_request` on this resource. | +| `canMerge` | [`Boolean!`](#boolean) | Indicates the user can perform `can_merge` on this resource. | +| `cherryPickOnCurrentMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `cherry_pick_on_current_merge_request` on this resource. | +| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource. | +| `pushToSourceBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `push_to_source_branch` on this resource. | +| `readMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `read_merge_request` on this resource. | +| `removeSourceBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_source_branch` on this resource. | +| `revertOnCurrentMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `revert_on_current_merge_request` on this resource. | +| `updateMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `update_merge_request` on this resource. | ### `MergeRequestReviewerRereviewPayload` @@ -4204,7 +4214,7 @@ Contains statistics about a milestone. | `complianceFrameworks` | [`ComplianceFrameworkConnection`](#complianceframeworkconnection) | Compliance frameworks available to projects in this namespace. Available only when feature flag `ff_custom_compliance_frameworks` is enabled. | | `containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. | | `description` | [`String`](#string) | Description of the namespace. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `fullName` | [`String!`](#string) | Full name of the namespace. | | `fullPath` | [`ID!`](#id) | Full path of the namespace. | | `id` | [`ID!`](#id) | ID of the namespace. | @@ -4258,7 +4268,7 @@ Autogenerated return type of NamespaceIncreaseStorageTemporarily. | ----- | ---- | ----------- | | `author` | [`User!`](#user) | User who wrote this note. | | `body` | [`String!`](#string) | Content of the note. | -| `bodyHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `note` | +| `bodyHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `note`. | | `confidential` | [`Boolean`](#boolean) | Indicates if this note is confidential. | | `createdAt` | [`Time!`](#time) | Timestamp of the note creation. | | `discussion` | [`Discussion`](#discussion) | The discussion this note is a part of. | @@ -4273,7 +4283,7 @@ Autogenerated return type of NamespaceIncreaseStorageTemporarily. | `systemNoteIconName` | [`String`](#string) | Name of the icon corresponding to a system note. | | `updatedAt` | [`Time!`](#time) | Timestamp of the note's last activity. | | `url` | [`String`](#string) | URL to view this Note in the Web UI. | -| `userPermissions` | [`NotePermissions!`](#notepermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`NotePermissions!`](#notepermissions) | Permissions for the current user on the resource. | ### `NoteConnection` @@ -4298,12 +4308,12 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminNote` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_note` on this resource | -| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource | -| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource | -| `readNote` | [`Boolean!`](#boolean) | Indicates the user can perform `read_note` on this resource | -| `repositionNote` | [`Boolean!`](#boolean) | Indicates the user can perform `reposition_note` on this resource | -| `resolveNote` | [`Boolean!`](#boolean) | Indicates the user can perform `resolve_note` on this resource | +| `adminNote` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_note` on this resource. | +| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource. | +| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource. | +| `readNote` | [`Boolean!`](#boolean) | Indicates the user can perform `read_note` on this resource. | +| `repositionNote` | [`Boolean!`](#boolean) | Indicates the user can perform `reposition_note` on this resource. | +| `resolveNote` | [`Boolean!`](#boolean) | Indicates the user can perform `resolve_note` on this resource. | ### `OncallParticipantType` @@ -4458,14 +4468,14 @@ Represents the Geo sync and verification state of a package file. | Field | Type | Description | | ----- | ---- | ----------- | -| `createdAt` | [`Time`](#time) | Timestamp when the PackageFileRegistry was created | -| `id` | [`ID!`](#id) | ID of the PackageFileRegistry | -| `lastSyncFailure` | [`String`](#string) | Error message during sync of the PackageFileRegistry | -| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the PackageFileRegistry | +| `createdAt` | [`Time`](#time) | Timestamp when the PackageFileRegistry was created. | +| `id` | [`ID!`](#id) | ID of the PackageFileRegistry. | +| `lastSyncFailure` | [`String`](#string) | Error message during sync of the PackageFileRegistry. | +| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the PackageFileRegistry. | | `packageFileId` | [`ID!`](#id) | ID of the PackageFile. | -| `retryAt` | [`Time`](#time) | Timestamp after which the PackageFileRegistry should be resynced | -| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the PackageFileRegistry | -| `state` | [`RegistryState`](#registrystate) | Sync state of the PackageFileRegistry | +| `retryAt` | [`Time`](#time) | Timestamp after which the PackageFileRegistry should be resynced. | +| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the PackageFileRegistry. | +| `state` | [`RegistryState`](#registrystate) | Sync state of the PackageFileRegistry. | ### `PackageFileRegistryConnection` @@ -4568,8 +4578,8 @@ Information about pagination in a connection. | Field | Type | Description | | ----- | ---- | ----------- | | `endCursor` | [`String`](#string) | When paginating forwards, the cursor to continue. | -| `hasNextPage` | [`Boolean!`](#boolean) | When paginating forwards, are there more items? | -| `hasPreviousPage` | [`Boolean!`](#boolean) | When paginating backwards, are there more items? | +| `hasNextPage` | [`Boolean!`](#boolean) | When paginating forwards, are there more items?. | +| `hasPreviousPage` | [`Boolean!`](#boolean) | When paginating backwards, are there more items?. | | `startCursor` | [`String`](#string) | When paginating backwards, the cursor to continue. | ### `Pipeline` @@ -4581,7 +4591,7 @@ Information about pagination in a connection. | `cancelable` | [`Boolean!`](#boolean) | Specifies if a pipeline can be canceled. | | `commitPath` | [`String`](#string) | Path to the commit that triggered the pipeline. | | `committedAt` | [`Time`](#time) | Timestamp of the pipeline's commit. | -| `configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE) | +| `configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). | | `coverage` | [`Float`](#float) | Coverage percentage. | | `createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. | | `detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. | @@ -4601,11 +4611,11 @@ Information about pagination in a connection. | `sourceJob` | [`CiJob`](#cijob) | Job where pipeline was triggered from. | | `stages` | [`CiStageConnection`](#cistageconnection) | Stages of the pipeline. | | `startedAt` | [`Time`](#time) | Timestamp when the pipeline was started. | -| `status` | [`PipelineStatusEnum!`](#pipelinestatusenum) | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED) | +| `status` | [`PipelineStatusEnum!`](#pipelinestatusenum) | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED). | | `updatedAt` | [`Time!`](#time) | Timestamp of the pipeline's last activity. | | `upstream` | [`Pipeline`](#pipeline) | Pipeline that triggered the pipeline. | | `user` | [`User`](#user) | Pipeline user. | -| `userPermissions` | [`PipelinePermissions!`](#pipelinepermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`PipelinePermissions!`](#pipelinepermissions) | Permissions for the current user on the resource. | | `warnings` | [`Boolean!`](#boolean) | Indicates if a pipeline has warnings. | ### `PipelineAnalytics` @@ -4666,9 +4676,9 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_pipeline` on this resource | -| `destroyPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_pipeline` on this resource | -| `updatePipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `update_pipeline` on this resource | +| `adminPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_pipeline` on this resource. | +| `destroyPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_pipeline` on this resource. | +| `updatePipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `update_pipeline` on this resource. | ### `PipelineRetryPayload` @@ -4752,7 +4762,7 @@ An edge in a connection. | `dastSiteProfiles` | [`DastSiteProfileConnection`](#dastsiteprofileconnection) | DAST Site Profiles associated with the project. | | `dastSiteValidations` | [`DastSiteValidationConnection`](#dastsitevalidationconnection) | DAST Site Validations associated with the project. | | `description` | [`String`](#string) | Short description of the project. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `environment` | [`Environment`](#environment) | A single environment of the project. | | `environments` | [`EnvironmentConnection`](#environmentconnection) | Environments of the project. | | `forksCount` | [`Int!`](#int) | Number of times the project has been forked. | @@ -4766,7 +4776,7 @@ An edge in a connection. | `issue` | [`Issue`](#issue) | A single issue of the project. | | `issueStatusCounts` | [`IssueStatusCountsType`](#issuestatuscountstype) | Counts of issues by status for the project. | | `issues` | [`IssueConnection`](#issueconnection) | Issues of the project. | -| `issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user | +| `issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user. | | `iterationCadences` | [`IterationCadenceConnection`](#iterationcadenceconnection) | Find iteration cadences. | | `iterations` | [`IterationConnection`](#iterationconnection) | Find iterations. | | `jiraImportStatus` | [`String`](#string) | Status of Jira import background job of the project. | @@ -4778,7 +4788,7 @@ An edge in a connection. | `lfsEnabled` | [`Boolean`](#boolean) | Indicates if the project has Large File Storage (LFS) enabled. | | `mergeRequest` | [`MergeRequest`](#mergerequest) | A single merge request of the project. | | `mergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests of the project. | -| `mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge Requests are enabled for the current user | +| `mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge Requests are enabled for the current user. | | `mergeRequestsFfOnlyEnabled` | [`Boolean`](#boolean) | Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. | | `milestones` | [`MilestoneConnection`](#milestoneconnection) | Milestones of the project. | | `name` | [`String!`](#string) | Name of the project (without namespace). | @@ -4815,7 +4825,7 @@ An edge in a connection. | `services` | [`ServiceConnection`](#serviceconnection) | Project services. | | `sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. | | `snippets` | [`SnippetConnection`](#snippetconnection) | Snippets of the project. | -| `snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user | +| `snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. | | `squashReadOnly` | [`Boolean!`](#boolean) | Indicates if `squashReadOnly` is enabled. | | `sshUrlToRepo` | [`String`](#string) | URL to connect to the project via SSH. | | `starCount` | [`Int!`](#int) | Number of times the project has been starred. | @@ -4824,14 +4834,14 @@ An edge in a connection. | `tagList` | [`String`](#string) | List of project topics (not Git tags). | | `terraformState` | [`TerraformState`](#terraformstate) | Find a single Terraform state by name. | | `terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. | -| `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. | | `visibility` | [`String`](#string) | Visibility of the project. | | `vulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Vulnerabilities reported on the project. | | `vulnerabilitiesCountByDay` | [`VulnerabilitiesCountByDayConnection`](#vulnerabilitiescountbydayconnection) | Number of vulnerabilities per day for the project. | | `vulnerabilityScanners` | [`VulnerabilityScannerConnection`](#vulnerabilityscannerconnection) | Vulnerability scanners reported on the project vulnerabilities. | | `vulnerabilitySeveritiesCount` | [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount) | Counts for each vulnerability severity in the project. | | `webUrl` | [`String`](#string) | Web URL of the project. | -| `wikiEnabled` | [`Boolean`](#boolean) | Indicates if Wikis are enabled for the current user | +| `wikiEnabled` | [`Boolean`](#boolean) | Indicates if Wikis are enabled for the current user. | ### `ProjectCiCdSetting` @@ -4875,7 +4885,7 @@ Represents a Project Membership. | `project` | [`Project`](#project) | Project that User is a member of. | | `updatedAt` | [`Time`](#time) | Date and time the membership was last updated. | | `user` | [`User!`](#user) | User that is associated with the member object. | -| `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. | ### `ProjectMemberConnection` @@ -4900,48 +4910,48 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminOperations` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_operations` on this resource | -| `adminProject` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_project` on this resource | -| `adminRemoteMirror` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_remote_mirror` on this resource | -| `adminWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_wiki` on this resource | -| `archiveProject` | [`Boolean!`](#boolean) | Indicates the user can perform `archive_project` on this resource | -| `changeNamespace` | [`Boolean!`](#boolean) | Indicates the user can perform `change_namespace` on this resource | -| `changeVisibilityLevel` | [`Boolean!`](#boolean) | Indicates the user can perform `change_visibility_level` on this resource | -| `createDeployment` | [`Boolean!`](#boolean) | Indicates the user can perform `create_deployment` on this resource | -| `createDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `create_design` on this resource | -| `createIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `create_issue` on this resource | -| `createLabel` | [`Boolean!`](#boolean) | Indicates the user can perform `create_label` on this resource | -| `createMergeRequestFrom` | [`Boolean!`](#boolean) | Indicates the user can perform `create_merge_request_from` on this resource | -| `createMergeRequestIn` | [`Boolean!`](#boolean) | Indicates the user can perform `create_merge_request_in` on this resource | -| `createPages` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pages` on this resource | -| `createPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pipeline` on this resource | -| `createPipelineSchedule` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pipeline_schedule` on this resource | -| `createSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `create_snippet` on this resource | -| `createWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `create_wiki` on this resource | -| `destroyDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_design` on this resource | -| `destroyPages` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_pages` on this resource | -| `destroyWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_wiki` on this resource | -| `downloadCode` | [`Boolean!`](#boolean) | Indicates the user can perform `download_code` on this resource | -| `downloadWikiCode` | [`Boolean!`](#boolean) | Indicates the user can perform `download_wiki_code` on this resource | -| `forkProject` | [`Boolean!`](#boolean) | Indicates the user can perform `fork_project` on this resource | -| `pushCode` | [`Boolean!`](#boolean) | Indicates the user can perform `push_code` on this resource | -| `pushToDeleteProtectedBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `push_to_delete_protected_branch` on this resource | -| `readCommitStatus` | [`Boolean!`](#boolean) | Indicates the user can perform `read_commit_status` on this resource | -| `readCycleAnalytics` | [`Boolean!`](#boolean) | Indicates the user can perform `read_cycle_analytics` on this resource | -| `readDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `read_design` on this resource | -| `readMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `read_merge_request` on this resource | -| `readPagesContent` | [`Boolean!`](#boolean) | Indicates the user can perform `read_pages_content` on this resource | -| `readProject` | [`Boolean!`](#boolean) | Indicates the user can perform `read_project` on this resource | -| `readProjectMember` | [`Boolean!`](#boolean) | Indicates the user can perform `read_project_member` on this resource | -| `readWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `read_wiki` on this resource | -| `removeForkProject` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_fork_project` on this resource | -| `removePages` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_pages` on this resource | -| `removeProject` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_project` on this resource | -| `renameProject` | [`Boolean!`](#boolean) | Indicates the user can perform `rename_project` on this resource | -| `requestAccess` | [`Boolean!`](#boolean) | Indicates the user can perform `request_access` on this resource | -| `updatePages` | [`Boolean!`](#boolean) | Indicates the user can perform `update_pages` on this resource | -| `updateWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `update_wiki` on this resource | -| `uploadFile` | [`Boolean!`](#boolean) | Indicates the user can perform `upload_file` on this resource | +| `adminOperations` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_operations` on this resource. | +| `adminProject` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_project` on this resource. | +| `adminRemoteMirror` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_remote_mirror` on this resource. | +| `adminWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_wiki` on this resource. | +| `archiveProject` | [`Boolean!`](#boolean) | Indicates the user can perform `archive_project` on this resource. | +| `changeNamespace` | [`Boolean!`](#boolean) | Indicates the user can perform `change_namespace` on this resource. | +| `changeVisibilityLevel` | [`Boolean!`](#boolean) | Indicates the user can perform `change_visibility_level` on this resource. | +| `createDeployment` | [`Boolean!`](#boolean) | Indicates the user can perform `create_deployment` on this resource. | +| `createDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `create_design` on this resource. | +| `createIssue` | [`Boolean!`](#boolean) | Indicates the user can perform `create_issue` on this resource. | +| `createLabel` | [`Boolean!`](#boolean) | Indicates the user can perform `create_label` on this resource. | +| `createMergeRequestFrom` | [`Boolean!`](#boolean) | Indicates the user can perform `create_merge_request_from` on this resource. | +| `createMergeRequestIn` | [`Boolean!`](#boolean) | Indicates the user can perform `create_merge_request_in` on this resource. | +| `createPages` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pages` on this resource. | +| `createPipeline` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pipeline` on this resource. | +| `createPipelineSchedule` | [`Boolean!`](#boolean) | Indicates the user can perform `create_pipeline_schedule` on this resource. | +| `createSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `create_snippet` on this resource. | +| `createWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `create_wiki` on this resource. | +| `destroyDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_design` on this resource. | +| `destroyPages` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_pages` on this resource. | +| `destroyWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_wiki` on this resource. | +| `downloadCode` | [`Boolean!`](#boolean) | Indicates the user can perform `download_code` on this resource. | +| `downloadWikiCode` | [`Boolean!`](#boolean) | Indicates the user can perform `download_wiki_code` on this resource. | +| `forkProject` | [`Boolean!`](#boolean) | Indicates the user can perform `fork_project` on this resource. | +| `pushCode` | [`Boolean!`](#boolean) | Indicates the user can perform `push_code` on this resource. | +| `pushToDeleteProtectedBranch` | [`Boolean!`](#boolean) | Indicates the user can perform `push_to_delete_protected_branch` on this resource. | +| `readCommitStatus` | [`Boolean!`](#boolean) | Indicates the user can perform `read_commit_status` on this resource. | +| `readCycleAnalytics` | [`Boolean!`](#boolean) | Indicates the user can perform `read_cycle_analytics` on this resource. | +| `readDesign` | [`Boolean!`](#boolean) | Indicates the user can perform `read_design` on this resource. | +| `readMergeRequest` | [`Boolean!`](#boolean) | Indicates the user can perform `read_merge_request` on this resource. | +| `readPagesContent` | [`Boolean!`](#boolean) | Indicates the user can perform `read_pages_content` on this resource. | +| `readProject` | [`Boolean!`](#boolean) | Indicates the user can perform `read_project` on this resource. | +| `readProjectMember` | [`Boolean!`](#boolean) | Indicates the user can perform `read_project_member` on this resource. | +| `readWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `read_wiki` on this resource. | +| `removeForkProject` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_fork_project` on this resource. | +| `removePages` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_pages` on this resource. | +| `removeProject` | [`Boolean!`](#boolean) | Indicates the user can perform `remove_project` on this resource. | +| `renameProject` | [`Boolean!`](#boolean) | Indicates the user can perform `rename_project` on this resource. | +| `requestAccess` | [`Boolean!`](#boolean) | Indicates the user can perform `request_access` on this resource. | +| `updatePages` | [`Boolean!`](#boolean) | Indicates the user can perform `update_pages` on this resource. | +| `updateWiki` | [`Boolean!`](#boolean) | Indicates the user can perform `update_wiki` on this resource. | +| `uploadFile` | [`Boolean!`](#boolean) | Indicates the user can perform `upload_file` on this resource. | ### `ProjectStatistics` @@ -5026,7 +5036,7 @@ Represents a release. | `commit` | [`Commit`](#commit) | The commit associated with the release. | | `createdAt` | [`Time`](#time) | Timestamp of when the release was created. | | `description` | [`String`](#string) | Description (also known as "release notes") of the release. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `evidences` | [`ReleaseEvidenceConnection`](#releaseevidenceconnection) | Evidence for the release. | | `links` | [`ReleaseLinks`](#releaselinks) | Links of the release. | | `milestones` | [`MilestoneConnection`](#milestoneconnection) | Milestones associated to the release. | @@ -5275,7 +5285,7 @@ Represents a requirement. | `author` | [`User!`](#user) | Author of the requirement. | | `createdAt` | [`Time!`](#time) | Timestamp of when the requirement was created. | | `description` | [`String`](#string) | Description of the requirement. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `id` | [`ID!`](#id) | ID of the requirement. | | `iid` | [`ID!`](#id) | Internal ID of the requirement. | | `lastTestReportManuallyCreated` | [`Boolean`](#boolean) | Indicates if latest test report was created by user. | @@ -5284,9 +5294,9 @@ Represents a requirement. | `state` | [`RequirementState!`](#requirementstate) | State of the requirement. | | `testReports` | [`TestReportConnection`](#testreportconnection) | Test reports of the requirement. | | `title` | [`String`](#string) | Title of the requirement. | -| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title` | +| `titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | `updatedAt` | [`Time!`](#time) | Timestamp of when the requirement was last updated. | -| `userPermissions` | [`RequirementPermissions!`](#requirementpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`RequirementPermissions!`](#requirementpermissions) | Permissions for the current user on the resource. | ### `RequirementConnection` @@ -5313,11 +5323,11 @@ Check permissions for the current user on a requirement. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_requirement` on this resource | -| `createRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `create_requirement` on this resource | -| `destroyRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_requirement` on this resource | -| `readRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `read_requirement` on this resource | -| `updateRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `update_requirement` on this resource | +| `adminRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_requirement` on this resource. | +| `createRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `create_requirement` on this resource. | +| `destroyRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_requirement` on this resource. | +| `readRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `read_requirement` on this resource. | +| `updateRequirement` | [`Boolean!`](#boolean) | Indicates the user can perform `update_requirement` on this resource. | ### `RequirementStatesCount` @@ -5587,13 +5597,13 @@ Represents summary of a security report. | Field | Type | Description | | ----- | ---- | ----------- | -| `apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan | -| `containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan | -| `coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan | -| `dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan | -| `dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan | -| `sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan | -| `secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan | +| `apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan. | +| `containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan. | +| `coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan. | +| `dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan. | +| `dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan. | +| `sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan. | +| `secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan. | ### `SecurityReportSummarySection` @@ -5779,11 +5789,11 @@ Represents a snippet entry. | Field | Type | Description | | ----- | ---- | ----------- | | `author` | [`User`](#user) | The owner of the snippet. | -| `blob` **{warning-solid}** | [`SnippetBlob!`](#snippetblob) | **Deprecated:** Use `blobs`. Deprecated in 13.3. | +| `blob` **{warning-solid}** | [`SnippetBlob!`](#snippetblob) | **Deprecated** in 13.3. Use `blobs`. | | `blobs` | [`SnippetBlobConnection`](#snippetblobconnection) | Snippet blobs. | | `createdAt` | [`Time!`](#time) | Timestamp this snippet was created. | | `description` | [`String`](#string) | Description of the snippet. | -| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description` | +| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. | | `discussions` | [`DiscussionConnection!`](#discussionconnection) | All discussions on this noteable. | | `fileName` | [`String`](#string) | File Name of the snippet. | | `httpUrlToRepo` | [`String`](#string) | HTTP URL to the snippet repository. | @@ -5794,7 +5804,7 @@ Represents a snippet entry. | `sshUrlToRepo` | [`String`](#string) | SSH URL to the snippet repository. | | `title` | [`String!`](#string) | Title of the snippet. | | `updatedAt` | [`Time!`](#time) | Timestamp this snippet was updated. | -| `userPermissions` | [`SnippetPermissions!`](#snippetpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`SnippetPermissions!`](#snippetpermissions) | Permissions for the current user on the resource. | | `visibilityLevel` | [`VisibilityLevelsEnum!`](#visibilitylevelsenum) | Visibility Level of the snippet. | | `webUrl` | [`String!`](#string) | Web URL of the snippet. | @@ -5873,12 +5883,12 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_snippet` on this resource | -| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource | -| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource | -| `readSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `read_snippet` on this resource | -| `reportSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `report_snippet` on this resource | -| `updateSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `update_snippet` on this resource | +| `adminSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_snippet` on this resource. | +| `awardEmoji` | [`Boolean!`](#boolean) | Indicates the user can perform `award_emoji` on this resource. | +| `createNote` | [`Boolean!`](#boolean) | Indicates the user can perform `create_note` on this resource. | +| `readSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `read_snippet` on this resource. | +| `reportSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `report_snippet` on this resource. | +| `updateSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `update_snippet` on this resource. | ### `SnippetRepositoryRegistry` @@ -5886,14 +5896,14 @@ Represents the Geo sync and verification state of a snippet repository. | Field | Type | Description | | ----- | ---- | ----------- | -| `createdAt` | [`Time`](#time) | Timestamp when the SnippetRepositoryRegistry was created | -| `id` | [`ID!`](#id) | ID of the SnippetRepositoryRegistry | -| `lastSyncFailure` | [`String`](#string) | Error message during sync of the SnippetRepositoryRegistry | -| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the SnippetRepositoryRegistry | -| `retryAt` | [`Time`](#time) | Timestamp after which the SnippetRepositoryRegistry should be resynced | -| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the SnippetRepositoryRegistry | +| `createdAt` | [`Time`](#time) | Timestamp when the SnippetRepositoryRegistry was created. | +| `id` | [`ID!`](#id) | ID of the SnippetRepositoryRegistry. | +| `lastSyncFailure` | [`String`](#string) | Error message during sync of the SnippetRepositoryRegistry. | +| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the SnippetRepositoryRegistry. | +| `retryAt` | [`Time`](#time) | Timestamp after which the SnippetRepositoryRegistry should be resynced. | +| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the SnippetRepositoryRegistry. | | `snippetRepositoryId` | [`ID!`](#id) | ID of the Snippet Repository. | -| `state` | [`RegistryState`](#registrystate) | Sync state of the SnippetRepositoryRegistry | +| `state` | [`RegistryState`](#registrystate) | Sync state of the SnippetRepositoryRegistry. | ### `SnippetRepositoryRegistryConnection` @@ -6042,13 +6052,13 @@ Represents the Geo sync and verification state of a terraform state version. | Field | Type | Description | | ----- | ---- | ----------- | -| `createdAt` | [`Time`](#time) | Timestamp when the TerraformStateVersionRegistry was created | -| `id` | [`ID!`](#id) | ID of the TerraformStateVersionRegistry | -| `lastSyncFailure` | [`String`](#string) | Error message during sync of the TerraformStateVersionRegistry | -| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the TerraformStateVersionRegistry | -| `retryAt` | [`Time`](#time) | Timestamp after which the TerraformStateVersionRegistry should be resynced | -| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the TerraformStateVersionRegistry | -| `state` | [`RegistryState`](#registrystate) | Sync state of the TerraformStateVersionRegistry | +| `createdAt` | [`Time`](#time) | Timestamp when the TerraformStateVersionRegistry was created. | +| `id` | [`ID!`](#id) | ID of the TerraformStateVersionRegistry. | +| `lastSyncFailure` | [`String`](#string) | Error message during sync of the TerraformStateVersionRegistry. | +| `lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the TerraformStateVersionRegistry. | +| `retryAt` | [`Time`](#time) | Timestamp after which the TerraformStateVersionRegistry should be resynced. | +| `retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the TerraformStateVersionRegistry. | +| `state` | [`RegistryState`](#registrystate) | Sync state of the TerraformStateVersionRegistry. | | `terraformStateVersionId` | [`ID!`](#id) | ID of the terraform state version. | ### `TerraformStateVersionRegistryConnection` @@ -6221,7 +6231,7 @@ Autogenerated return type of TodoRestoreMany. | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `todos` | [`[Todo!]!`](#todo) | Updated to-do items. | -| `updatedIds` **{warning-solid}** | [`[TodoID!]!`](#todoid) | **Deprecated:** Use to-do items. Deprecated in 13.2. | +| `updatedIds` **{warning-solid}** | [`[TodoID!]!`](#todoid) | **Deprecated** in 13.2. Use to-do items. | ### `TodoRestorePayload` @@ -6242,7 +6252,7 @@ Autogenerated return type of TodosMarkAllDone. | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `todos` | [`[Todo!]!`](#todo) | Updated to-do items. | -| `updatedIds` **{warning-solid}** | [`[TodoID!]!`](#todoid) | **Deprecated:** Use to-do items. Deprecated in 13.2. | +| `updatedIds` **{warning-solid}** | [`[TodoID!]!`](#todoid) | **Deprecated** in 13.2. Use to-do items. | ### `ToggleAwardEmojiPayload` @@ -6484,7 +6494,7 @@ Representation of a GitLab user. | `avatarUrl` | [`String`](#string) | URL of the user's avatar. | | `bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. | | `callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. | -| `email` **{warning-solid}** | [`String`](#string) | **Deprecated:** Use public_email. Deprecated in 13.7. | +| `email` **{warning-solid}** | [`String`](#string) | **Deprecated** in 13.7. This was renamed. Use: `User.publicEmail`. | | `groupCount` | [`Int`](#int) | Group count for the user. Available only when feature flag `user_group_counts` is enabled. | | `groupMemberships` | [`GroupMemberConnection`](#groupmemberconnection) | Group memberships of the user. | | `id` | [`ID!`](#id) | ID of the user. | @@ -6498,7 +6508,7 @@ Representation of a GitLab user. | `state` | [`UserState!`](#userstate) | State of the user. | | `status` | [`UserStatus`](#userstatus) | User status. | | `todos` | [`TodoConnection!`](#todoconnection) | To-do items of the user. | -| `userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. | | `username` | [`String!`](#string) | Username of the user. Unique within this instance of GitLab. | | `webPath` | [`String!`](#string) | Web path of the user. | | `webUrl` | [`String!`](#string) | Web URL of the user. | @@ -6562,7 +6572,7 @@ An edge in a connection. | Field | Type | Description | | ----- | ---- | ----------- | -| `createSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `create_snippet` on this resource | +| `createSnippet` | [`Boolean!`](#boolean) | Indicates the user can perform `create_snippet` on this resource. | ### `UserStatus` @@ -6571,7 +6581,7 @@ An edge in a connection. | `availability` | [`AvailabilityEnum!`](#availabilityenum) | User availability status. | | `emoji` | [`String`](#string) | String representation of emoji. | | `message` | [`String`](#string) | User status message. | -| `messageHtml` | [`String`](#string) | HTML of the user status message | +| `messageHtml` | [`String`](#string) | HTML of the user status message. | ### `VulnerabilitiesCountByDay` @@ -6579,14 +6589,14 @@ Represents the count of vulnerabilities by severity on a particular day. This da | Field | Type | Description | | ----- | ---- | ----------- | -| `critical` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with critical severity | +| `critical` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with critical severity. | | `date` | [`ISO8601Date!`](#iso8601date) | Date for the count. | -| `high` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with high severity | -| `info` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with info severity | -| `low` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with low severity | -| `medium` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with medium severity | +| `high` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with high severity. | +| `info` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with info severity. | +| `low` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with low severity. | +| `medium` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with medium severity. | | `total` | [`Int!`](#int) | Total number of vulnerabilities on a particular day. | -| `unknown` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with unknown severity | +| `unknown` | [`Int!`](#int) | Total number of vulnerabilities on a particular day with unknown severity. | ### `VulnerabilitiesCountByDayAndSeverity` @@ -6665,11 +6675,11 @@ Represents a vulnerability. | `resolvedBy` | [`User`](#user) | The user that resolved the vulnerability. | | `resolvedOnDefaultBranch` | [`Boolean!`](#boolean) | Indicates whether the vulnerability is fixed on the default branch or not. | | `scanner` | [`VulnerabilityScanner`](#vulnerabilityscanner) | Scanner metadata for the vulnerability. | -| `severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) | -| `state` | [`VulnerabilityState`](#vulnerabilitystate) | State of the vulnerability (DETECTED, CONFIRMED, RESOLVED, DISMISSED) | +| `severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL). | +| `state` | [`VulnerabilityState`](#vulnerabilitystate) | State of the vulnerability (DETECTED, CONFIRMED, RESOLVED, DISMISSED). | | `title` | [`String`](#string) | Title of the vulnerability. | | `userNotesCount` | [`Int!`](#int) | Number of user notes attached to the vulnerability. | -| `userPermissions` | [`VulnerabilityPermissions!`](#vulnerabilitypermissions) | Permissions for the current user on the resource | +| `userPermissions` | [`VulnerabilityPermissions!`](#vulnerabilitypermissions) | Permissions for the current user on the resource. | | `vulnerabilityPath` | [`String`](#string) | URL to the vulnerability's details page. | ### `VulnerabilityConfirmPayload` @@ -7024,15 +7034,15 @@ Check permissions for the current user on a vulnerability. | Field | Type | Description | | ----- | ---- | ----------- | -| `adminVulnerability` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability` on this resource | -| `adminVulnerabilityExternalIssueLink` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability_external_issue_link` on this resource | -| `adminVulnerabilityIssueLink` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability_issue_link` on this resource | -| `createVulnerability` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability` on this resource | -| `createVulnerabilityExport` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability_export` on this resource | -| `createVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability_feedback` on this resource | -| `destroyVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_vulnerability_feedback` on this resource | -| `readVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `read_vulnerability_feedback` on this resource | -| `updateVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `update_vulnerability_feedback` on this resource | +| `adminVulnerability` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability` on this resource. | +| `adminVulnerabilityExternalIssueLink` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability_external_issue_link` on this resource. | +| `adminVulnerabilityIssueLink` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_vulnerability_issue_link` on this resource. | +| `createVulnerability` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability` on this resource. | +| `createVulnerabilityExport` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability_export` on this resource. | +| `createVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `create_vulnerability_feedback` on this resource. | +| `destroyVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `destroy_vulnerability_feedback` on this resource. | +| `readVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `read_vulnerability_feedback` on this resource. | +| `updateVulnerabilityFeedback` | [`Boolean!`](#boolean) | Indicates the user can perform `update_vulnerability_feedback` on this resource. | ### `VulnerabilityResolvePayload` @@ -7090,12 +7100,12 @@ Represents vulnerability counts by severity. | Field | Type | Description | | ----- | ---- | ----------- | -| `critical` | [`Int`](#int) | Number of vulnerabilities of CRITICAL severity of the project | -| `high` | [`Int`](#int) | Number of vulnerabilities of HIGH severity of the project | -| `info` | [`Int`](#int) | Number of vulnerabilities of INFO severity of the project | -| `low` | [`Int`](#int) | Number of vulnerabilities of LOW severity of the project | -| `medium` | [`Int`](#int) | Number of vulnerabilities of MEDIUM severity of the project | -| `unknown` | [`Int`](#int) | Number of vulnerabilities of UNKNOWN severity of the project | +| `critical` | [`Int`](#int) | Number of vulnerabilities of CRITICAL severity of the project. | +| `high` | [`Int`](#int) | Number of vulnerabilities of HIGH severity of the project. | +| `info` | [`Int`](#int) | Number of vulnerabilities of INFO severity of the project. | +| `low` | [`Int`](#int) | Number of vulnerabilities of LOW severity of the project. | +| `medium` | [`Int`](#int) | Number of vulnerabilities of MEDIUM severity of the project. | +| `unknown` | [`Int`](#int) | Number of vulnerabilities of UNKNOWN severity of the project. | ### `VulnerableDependency` @@ -7139,13 +7149,13 @@ Access level to a resource. | Value | Description | | ----- | ----------- | -| `DEVELOPER` | Developer access | -| `GUEST` | Guest access | -| `MAINTAINER` | Maintainer access | -| `MINIMAL_ACCESS` | Minimal access | -| `NO_ACCESS` | No access | -| `OWNER` | Owner access | -| `REPORTER` | Reporter access | +| `DEVELOPER` | Developer access. | +| `GUEST` | Guest access. | +| `MAINTAINER` | Maintainer access. | +| `MINIMAL_ACCESS` | Minimal access. | +| `NO_ACCESS` | No access. | +| `OWNER` | Owner access. | +| `REPORTER` | Reporter access. | ### `AlertManagementAlertSort` @@ -7171,10 +7181,10 @@ Values for sorting alerts. | `UPDATED_DESC` | Updated at descending order. | | `UPDATED_TIME_ASC` | Created time by ascending order. | | `UPDATED_TIME_DESC` | Created time by descending order. | -| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. | -| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. | -| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. | -| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. | +| `created_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_ASC`. Deprecated in 13.5. | +| `created_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_DESC`. Deprecated in 13.5. | +| `updated_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_ASC`. Deprecated in 13.5. | +| `updated_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_DESC`. Deprecated in 13.5. | ### `AlertManagementDomainFilter` @@ -7227,12 +7237,12 @@ Alert severity values. | Value | Description | | ----- | ----------- | -| `CRITICAL` | Critical severity | -| `HIGH` | High severity | -| `INFO` | Info severity | -| `LOW` | Low severity | -| `MEDIUM` | Medium severity | -| `UNKNOWN` | Unknown severity | +| `CRITICAL` | Critical severity. | +| `HIGH` | High severity. | +| `INFO` | Info severity. | +| `LOW` | Low severity. | +| `MEDIUM` | Medium severity. | +| `UNKNOWN` | Unknown severity. | ### `AlertManagementStatus` @@ -7240,10 +7250,10 @@ Alert status values. | Value | Description | | ----- | ----------- | -| `ACKNOWLEDGED` | Acknowledged status | -| `IGNORED` | Ignored status | -| `RESOLVED` | Resolved status | -| `TRIGGERED` | Triggered status | +| `ACKNOWLEDGED` | Acknowledged status. | +| `IGNORED` | Ignored status. | +| `RESOLVED` | Resolved status. | +| `TRIGGERED` | Triggered status. | ### `ApiFuzzingScanMode` @@ -7261,8 +7271,8 @@ User availability status. | Value | Description | | ----- | ----------- | -| `BUSY` | Busy | -| `NOT_SET` | Not Set | +| `BUSY` | Busy. | +| `NOT_SET` | Not Set. | ### `BlobViewersType` @@ -7322,31 +7332,31 @@ Mode of a commit action. | Value | Description | | ----- | ----------- | -| `EVERY_DAY` | Every day | -| `EVERY_MONTH` | Every month | -| `EVERY_THREE_MONTHS` | Every three months | -| `EVERY_TWO_WEEKS` | Every two weeks | -| `EVERY_WEEK` | Every week | +| `EVERY_DAY` | Every day. | +| `EVERY_MONTH` | Every month. | +| `EVERY_THREE_MONTHS` | Every three months. | +| `EVERY_TWO_WEEKS` | Every two weeks. | +| `EVERY_WEEK` | Every week. | ### `ContainerExpirationPolicyKeepEnum` | Value | Description | | ----- | ----------- | -| `FIFTY_TAGS` | 50 tags per image name | -| `FIVE_TAGS` | 5 tags per image name | -| `ONE_HUNDRED_TAGS` | 100 tags per image name | -| `ONE_TAG` | 1 tag per image name | -| `TEN_TAGS` | 10 tags per image name | -| `TWENTY_FIVE_TAGS` | 25 tags per image name | +| `FIFTY_TAGS` | 50 tags per image name. | +| `FIVE_TAGS` | 5 tags per image name. | +| `ONE_HUNDRED_TAGS` | 100 tags per image name. | +| `ONE_TAG` | 1 tag per image name. | +| `TEN_TAGS` | 10 tags per image name. | +| `TWENTY_FIVE_TAGS` | 25 tags per image name. | ### `ContainerExpirationPolicyOlderThanEnum` | Value | Description | | ----- | ----------- | -| `FOURTEEN_DAYS` | 14 days until tags are automatically removed | -| `NINETY_DAYS` | 90 days until tags are automatically removed | -| `SEVEN_DAYS` | 7 days until tags are automatically removed | -| `THIRTY_DAYS` | 30 days until tags are automatically removed | +| `FOURTEEN_DAYS` | 14 days until tags are automatically removed. | +| `NINETY_DAYS` | 90 days until tags are automatically removed. | +| `SEVEN_DAYS` | 7 days until tags are automatically removed. | +| `THIRTY_DAYS` | 30 days until tags are automatically removed. | ### `ContainerRepositoryCleanupStatus` @@ -7371,10 +7381,10 @@ Values for sorting container repositories. | `NAME_DESC` | Name by descending order. | | `UPDATED_ASC` | Updated at ascending order. | | `UPDATED_DESC` | Updated at descending order. | -| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. | -| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. | -| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. | -| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. | +| `created_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_ASC`. Deprecated in 13.5. | +| `created_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_DESC`. Deprecated in 13.5. | +| `updated_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_ASC`. Deprecated in 13.5. | +| `updated_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_DESC`. Deprecated in 13.5. | ### `ContainerRepositoryStatus` @@ -7415,11 +7425,11 @@ Color of the data visualization palette. | Value | Description | | ----- | ----------- | -| `AQUA` | Aqua color | -| `BLUE` | Blue color | -| `GREEN` | Green color | -| `MAGENTA` | Magenta color | -| `ORANGE` | Orange color | +| `AQUA` | Aqua color. | +| `BLUE` | Blue color. | +| `GREEN` | Green color. | +| `MAGENTA` | Magenta color. | +| `ORANGE` | Orange color. | ### `DataVisualizationWeightEnum` @@ -7427,17 +7437,17 @@ Weight of the data visualization palette. | Value | Description | | ----- | ----------- | -| `WEIGHT_100` | 100 weight | -| `WEIGHT_200` | 200 weight | -| `WEIGHT_300` | 300 weight | -| `WEIGHT_400` | 400 weight | -| `WEIGHT_50` | 50 weight | -| `WEIGHT_500` | 500 weight | -| `WEIGHT_600` | 600 weight | -| `WEIGHT_700` | 700 weight | -| `WEIGHT_800` | 800 weight | -| `WEIGHT_900` | 900 weight | -| `WEIGHT_950` | 950 weight | +| `WEIGHT_100` | 100 weight. | +| `WEIGHT_200` | 200 weight. | +| `WEIGHT_300` | 300 weight. | +| `WEIGHT_400` | 400 weight. | +| `WEIGHT_50` | 50 weight. | +| `WEIGHT_500` | 500 weight. | +| `WEIGHT_600` | 600 weight. | +| `WEIGHT_700` | 700 weight. | +| `WEIGHT_800` | 800 weight. | +| `WEIGHT_900` | 900 weight. | +| `WEIGHT_950` | 950 weight. | ### `DesignCollectionCopyState` @@ -7445,9 +7455,9 @@ Copy state of a DesignCollection. | Value | Description | | ----- | ----------- | -| `ERROR` | The DesignCollection encountered an error during a copy | -| `IN_PROGRESS` | The DesignCollection is being copied | -| `READY` | The DesignCollection has no copy in progress | +| `ERROR` | The DesignCollection encountered an error during a copy. | +| `IN_PROGRESS` | The DesignCollection is being copied. | +| `READY` | The DesignCollection has no copy in progress. | ### `DesignVersionEvent` @@ -7455,9 +7465,9 @@ Mutation event of a design within a version. | Value | Description | | ----- | ----------- | -| `CREATION` | A creation event | -| `DELETION` | A deletion event | -| `MODIFICATION` | A modification event | +| `CREATION` | A creation event. | +| `DELETION` | A deletion event. | +| `MODIFICATION` | A modification event. | | `NONE` | No change. | ### `DiffPositionType` @@ -7466,8 +7476,8 @@ Type of file the position refers to. | Value | Description | | ----- | ----------- | -| `image` | An image | -| `text` | A text file | +| `image` | An image. | +| `text` | A text file. | ### `EntryType` @@ -7528,19 +7538,19 @@ Event action. | Value | Description | | ----- | ----------- | -| `APPROVED` | Approved action | -| `ARCHIVED` | Archived action | -| `CLOSED` | Closed action | -| `COMMENTED` | Commented action | -| `CREATED` | Created action | -| `DESTROYED` | Destroyed action | -| `EXPIRED` | Expired action | -| `JOINED` | Joined action | -| `LEFT` | Left action | -| `MERGED` | Merged action | -| `PUSHED` | Pushed action | -| `REOPENED` | Reopened action | -| `UPDATED` | Updated action | +| `APPROVED` | Approved action. | +| `ARCHIVED` | Archived action. | +| `CLOSED` | Closed action. | +| `COMMENTED` | Commented action. | +| `CREATED` | Created action. | +| `DESTROYED` | Destroyed action. | +| `EXPIRED` | Expired action. | +| `JOINED` | Joined action. | +| `LEFT` | Left action. | +| `MERGED` | Merged action. | +| `PUSHED` | Pushed action. | +| `REOPENED` | Reopened action. | +| `UPDATED` | Updated action. | ### `GroupMemberRelation` @@ -7548,9 +7558,9 @@ Group member relation. | Value | Description | | ----- | ----------- | -| `DESCENDANTS` | Descendants members | -| `DIRECT` | Direct members | -| `INHERITED` | Inherited members | +| `DESCENDANTS` | Descendants members. | +| `DIRECT` | Direct members. | +| `INHERITED` | Inherited members. | ### `HealthStatus` @@ -7568,11 +7578,11 @@ Incident severity. | Value | Description | | ----- | ----------- | -| `CRITICAL` | Critical severity | -| `HIGH` | High severity | -| `LOW` | Low severity | -| `MEDIUM` | Medium severity | -| `UNKNOWN` | Unknown severity | +| `CRITICAL` | Critical severity. | +| `HIGH` | High severity. | +| `LOW` | Low severity. | +| `MEDIUM` | Medium severity. | +| `UNKNOWN` | Unknown severity. | ### `IssuableState` @@ -7612,10 +7622,10 @@ Values for sorting issues. | `UPDATED_DESC` | Updated at descending order. | | `WEIGHT_ASC` | Weight by ascending order. | | `WEIGHT_DESC` | Weight by descending order. | -| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. | -| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. | -| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. | -| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. | +| `created_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_ASC`. Deprecated in 13.5. | +| `created_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_DESC`. Deprecated in 13.5. | +| `updated_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_ASC`. Deprecated in 13.5. | +| `updated_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_DESC`. Deprecated in 13.5. | ### `IssueState` @@ -7643,9 +7653,9 @@ Issue type. | Value | Description | | ----- | ----------- | -| `INCIDENT` | Incident issue type | -| `ISSUE` | Issue issue type | -| `TEST_CASE` | Test Case issue type | +| `INCIDENT` | Incident issue type. | +| `ISSUE` | Issue issue type. | +| `TEST_CASE` | Test Case issue type. | ### `IterationState` @@ -7755,10 +7765,10 @@ Values for sorting merge requests. | `PRIORITY_DESC` | Priority by descending order. | | `UPDATED_ASC` | Updated at ascending order. | | `UPDATED_DESC` | Updated at descending order. | -| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. | -| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. | -| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. | -| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. | +| `created_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_ASC`. Deprecated in 13.5. | +| `created_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_DESC`. Deprecated in 13.5. | +| `updated_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_ASC`. Deprecated in 13.5. | +| `updated_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_DESC`. Deprecated in 13.5. | ### `MergeRequestState` @@ -7823,24 +7833,24 @@ Rotation length unit of an on-call rotation. | Value | Description | | ----- | ----------- | -| `DAYS` | Days | -| `HOURS` | Hours | -| `WEEKS` | Weeks | +| `DAYS` | Days. | +| `HOURS` | Hours. | +| `WEEKS` | Weeks. | ### `PackageTypeEnum` | Value | Description | | ----- | ----------- | -| `COMPOSER` | Packages from the Composer package manager | -| `CONAN` | Packages from the Conan package manager | -| `DEBIAN` | Packages from the Debian package manager | -| `GENERIC` | Packages from the Generic package manager | -| `GOLANG` | Packages from the Golang package manager | -| `MAVEN` | Packages from the Maven package manager | -| `NPM` | Packages from the npm package manager | -| `NUGET` | Packages from the Nuget package manager | -| `PYPI` | Packages from the PyPI package manager | -| `RUBYGEMS` | Packages from the Rubygems package manager | +| `COMPOSER` | Packages from the Composer package manager. | +| `CONAN` | Packages from the Conan package manager. | +| `DEBIAN` | Packages from the Debian package manager. | +| `GENERIC` | Packages from the Generic package manager. | +| `GOLANG` | Packages from the Golang package manager. | +| `MAVEN` | Packages from the Maven package manager. | +| `NPM` | Packages from the npm package manager. | +| `NUGET` | Packages from the Nuget package manager. | +| `PYPI` | Packages from the PyPI package manager. | +| `RUBYGEMS` | Packages from the Rubygems package manager. | ### `PipelineConfigSourceEnum` @@ -7878,10 +7888,10 @@ Project member relation. | Value | Description | | ----- | ----------- | -| `DESCENDANTS` | Descendants members | -| `DIRECT` | Direct members | -| `INHERITED` | Inherited members | -| `INVITED_GROUPS` | Invited Groups members | +| `DESCENDANTS` | Descendants members. | +| `DIRECT` | Direct members. | +| `INHERITED` | Inherited members. | +| `INVITED_GROUPS` | Invited Groups members. | ### `RegistryState` @@ -7900,10 +7910,10 @@ Type of the link: `other`, `runbook`, `image`, `package`. | Value | Description | | ----- | ----------- | -| `IMAGE` | Image link type | -| `OTHER` | Other link type | -| `PACKAGE` | Package link type | -| `RUNBOOK` | Runbook link type | +| `IMAGE` | Image link type. | +| `OTHER` | Other link type. | +| `PACKAGE` | Package link type. | +| `RUNBOOK` | Runbook link type. | ### `ReleaseSort` @@ -7949,13 +7959,13 @@ Size of UI component in SAST configuration page. | Value | Description | | ----- | ----------- | -| `API_FUZZING` | API FUZZING scan report | -| `CONTAINER_SCANNING` | CONTAINER SCANNING scan report | -| `COVERAGE_FUZZING` | COVERAGE FUZZING scan report | -| `DAST` | DAST scan report | -| `DEPENDENCY_SCANNING` | DEPENDENCY SCANNING scan report | -| `SAST` | SAST scan report | -| `SECRET_DETECTION` | SECRET DETECTION scan report | +| `API_FUZZING` | API FUZZING scan report. | +| `CONTAINER_SCANNING` | CONTAINER SCANNING scan report. | +| `COVERAGE_FUZZING` | COVERAGE FUZZING scan report. | +| `DAST` | DAST scan report. | +| `DEPENDENCY_SCANNING` | DEPENDENCY SCANNING scan report. | +| `SAST` | SAST scan report. | +| `SECRET_DETECTION` | SECRET DETECTION scan report. | ### `SecurityScannerType` @@ -7986,42 +7996,42 @@ State of a Sentry error. | Value | Description | | ----- | ----------- | -| `ASANA_SERVICE` | AsanaService type | -| `ASSEMBLA_SERVICE` | AssemblaService type | -| `BAMBOO_SERVICE` | BambooService type | -| `BUGZILLA_SERVICE` | BugzillaService type | -| `BUILDKITE_SERVICE` | BuildkiteService type | -| `CAMPFIRE_SERVICE` | CampfireService type | -| `CONFLUENCE_SERVICE` | ConfluenceService type | -| `CUSTOM_ISSUE_TRACKER_SERVICE` | CustomIssueTrackerService type | -| `DATADOG_SERVICE` | DatadogService type | -| `DISCORD_SERVICE` | DiscordService type | -| `DRONE_CI_SERVICE` | DroneCiService type | -| `EMAILS_ON_PUSH_SERVICE` | EmailsOnPushService type | -| `EWM_SERVICE` | EwmService type | -| `EXTERNAL_WIKI_SERVICE` | ExternalWikiService type | -| `FLOWDOCK_SERVICE` | FlowdockService type | -| `GITHUB_SERVICE` | GithubService type | -| `HANGOUTS_CHAT_SERVICE` | HangoutsChatService type | -| `HIPCHAT_SERVICE` | HipchatService type | -| `IRKER_SERVICE` | IrkerService type | -| `JENKINS_SERVICE` | JenkinsService type | -| `JIRA_SERVICE` | JiraService type | -| `MATTERMOST_SERVICE` | MattermostService type | -| `MATTERMOST_SLASH_COMMANDS_SERVICE` | MattermostSlashCommandsService type | -| `MICROSOFT_TEAMS_SERVICE` | MicrosoftTeamsService type | -| `PACKAGIST_SERVICE` | PackagistService type | -| `PIPELINES_EMAIL_SERVICE` | PipelinesEmailService type | -| `PIVOTALTRACKER_SERVICE` | PivotaltrackerService type | -| `PROMETHEUS_SERVICE` | PrometheusService type | -| `PUSHOVER_SERVICE` | PushoverService type | -| `REDMINE_SERVICE` | RedmineService type | -| `SLACK_SERVICE` | SlackService type | -| `SLACK_SLASH_COMMANDS_SERVICE` | SlackSlashCommandsService type | -| `TEAMCITY_SERVICE` | TeamcityService type | -| `UNIFY_CIRCUIT_SERVICE` | UnifyCircuitService type | -| `WEBEX_TEAMS_SERVICE` | WebexTeamsService type | -| `YOUTRACK_SERVICE` | YoutrackService type | +| `ASANA_SERVICE` | AsanaService type. | +| `ASSEMBLA_SERVICE` | AssemblaService type. | +| `BAMBOO_SERVICE` | BambooService type. | +| `BUGZILLA_SERVICE` | BugzillaService type. | +| `BUILDKITE_SERVICE` | BuildkiteService type. | +| `CAMPFIRE_SERVICE` | CampfireService type. | +| `CONFLUENCE_SERVICE` | ConfluenceService type. | +| `CUSTOM_ISSUE_TRACKER_SERVICE` | CustomIssueTrackerService type. | +| `DATADOG_SERVICE` | DatadogService type. | +| `DISCORD_SERVICE` | DiscordService type. | +| `DRONE_CI_SERVICE` | DroneCiService type. | +| `EMAILS_ON_PUSH_SERVICE` | EmailsOnPushService type. | +| `EWM_SERVICE` | EwmService type. | +| `EXTERNAL_WIKI_SERVICE` | ExternalWikiService type. | +| `FLOWDOCK_SERVICE` | FlowdockService type. | +| `GITHUB_SERVICE` | GithubService type. | +| `HANGOUTS_CHAT_SERVICE` | HangoutsChatService type. | +| `HIPCHAT_SERVICE` | HipchatService type. | +| `IRKER_SERVICE` | IrkerService type. | +| `JENKINS_SERVICE` | JenkinsService type. | +| `JIRA_SERVICE` | JiraService type. | +| `MATTERMOST_SERVICE` | MattermostService type. | +| `MATTERMOST_SLASH_COMMANDS_SERVICE` | MattermostSlashCommandsService type. | +| `MICROSOFT_TEAMS_SERVICE` | MicrosoftTeamsService type. | +| `PACKAGIST_SERVICE` | PackagistService type. | +| `PIPELINES_EMAIL_SERVICE` | PipelinesEmailService type. | +| `PIVOTALTRACKER_SERVICE` | PivotaltrackerService type. | +| `PROMETHEUS_SERVICE` | PrometheusService type. | +| `PUSHOVER_SERVICE` | PushoverService type. | +| `REDMINE_SERVICE` | RedmineService type. | +| `SLACK_SERVICE` | SlackService type. | +| `SLACK_SLASH_COMMANDS_SERVICE` | SlackSlashCommandsService type. | +| `TEAMCITY_SERVICE` | TeamcityService type. | +| `UNIFY_CIRCUIT_SERVICE` | UnifyCircuitService type. | +| `WEBEX_TEAMS_SERVICE` | WebexTeamsService type. | +| `YOUTRACK_SERVICE` | YoutrackService type. | ### `SnippetBlobActionEnum` @@ -8044,10 +8054,10 @@ Common sort values. | `CREATED_DESC` | Created at descending order. | | `UPDATED_ASC` | Updated at ascending order. | | `UPDATED_DESC` | Updated at descending order. | -| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. | -| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. | -| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. | -| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. | +| `created_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_ASC`. Deprecated in 13.5. | +| `created_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `CREATED_DESC`. Deprecated in 13.5. | +| `updated_asc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_ASC`. Deprecated in 13.5. | +| `updated_desc` **{warning-solid}** | **Deprecated:** This was renamed. Please use `UPDATED_DESC`. Deprecated in 13.5. | ### `TestReportState` @@ -8174,7 +8184,7 @@ The external tracker of the external issue link related to a vulnerability. | Value | Description | | ----- | ----------- | -| `JIRA` | Jira external tracker | +| `JIRA` | Jira external tracker. | ### `VulnerabilityExternalIssueLinkType` @@ -8182,7 +8192,7 @@ The type of the external issue link related to a vulnerability. | Value | Description | | ----- | ----------- | -| `CREATED` | Created link type | +| `CREATED` | Created link type. | ### `VulnerabilityGrade` @@ -8750,7 +8760,7 @@ Implementations: | `fullPath` | [`String!`](#string) | The full path to the design file. | | `id` | [`ID!`](#id) | The ID of this design. | | `image` | [`String!`](#string) | The URL of the full-sized image. | -| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated | +| `imageV432x230` | [`String`](#string) | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated. | | `issue` | [`Issue!`](#issue) | The issue the design belongs to. | | `notesCount` | [`Int!`](#int) | The total count of user-created notes for this design. | | `project` | [`Project!`](#project) | The project the design belongs to. | diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md index 73c7f0eb91f..c66f9038ed2 100644 --- a/doc/user/project/protected_branches.md +++ b/doc/user/project/protected_branches.md @@ -177,13 +177,12 @@ Deleting a protected branch is allowed only by using the web interface; not from This means that you can't accidentally delete a protected branch from your command line or a Git client application. -## Allow force push on protected branches **(FREE SELF)** +## Allow force push on protected branches -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15611) in GitLab 13.10. -> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. -> - It's disabled on GitLab.com. -> - It's not recommended for production use. -> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-allow-force-push-on-protected-branches). +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15611) in GitLab 13.10 behind a disabled feature flag. +> - It's enabled on GitLab.com. +> - It's recommended for production use. +> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-allow-force-push-on-protected-branches). WARNING: This feature might not be available to you. Check the **version history** note above for details. @@ -253,8 +252,8 @@ for details about the pipelines security model. ## Enable or disable allow force push on protected branches **(FREE SELF)** -Allow force push on protected branches is under development and not ready for -production use. It is deployed behind a feature flag that is **disabled by default**. +Allow force push on protected branches is ready for +production use. It is deployed behind a feature flag that is **enabled by default**. [GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) can enable it. diff --git a/lib/feature.rb b/lib/feature.rb index ad243719096..7c926b25587 100644 --- a/lib/feature.rb +++ b/lib/feature.rb @@ -57,7 +57,7 @@ class Feature # use `default_enabled: true` to default the flag to being `enabled` # unless set explicitly. The default is `disabled` # TODO: remove the `default_enabled:` and read it from the `defintion_yaml` - # check: https://gitlab.com/gitlab-org/gitlab/-/issues/271275 + # check: https://gitlab.com/gitlab-org/gitlab/-/issues/30228 def enabled?(key, thing = nil, type: :development, default_enabled: false) if check_feature_flags_definition? if thing && !thing.respond_to?(:flipper_id) @@ -65,11 +65,11 @@ class Feature "The thing '#{thing.class.name}' for feature flag '#{key}' needs to include `FeatureGate` or implement `flipper_id`" end - Feature::Definition.valid_usage!(key, type: type, default_enabled: :yaml) + Feature::Definition.valid_usage!(key, type: type, default_enabled: default_enabled) end - # TODO: Remove rubocop disable comment once `default_enabled` argument is removed https://gitlab.com/gitlab-org/gitlab/-/issues/271275 - default_enabled = Feature::Definition.default_enabled?(key) # rubocop:disable Lint/ShadowedArgument + # If `default_enabled: :yaml` we fetch the value from the YAML definition instead. + default_enabled = Feature::Definition.default_enabled?(key) if default_enabled == :yaml # During setup the database does not exist yet. So we haven't stored a value # for the feature yet and return the default. diff --git a/lib/gitlab/ci/reports/codequality_reports.rb b/lib/gitlab/ci/reports/codequality_reports.rb index ed7373a7d4b..27c41c384b8 100644 --- a/lib/gitlab/ci/reports/codequality_reports.rb +++ b/lib/gitlab/ci/reports/codequality_reports.rb @@ -6,6 +6,7 @@ module Gitlab class CodequalityReports attr_reader :degradations, :error_message + SEVERITY_PRIORITIES = %w(blocker critical major minor info).map.with_index.to_h.freeze # { "blocker" => 0, "critical" => 1 ... } CODECLIMATE_SCHEMA_PATH = Rails.root.join('app', 'validators', 'json_schemas', 'codeclimate.json').to_s def initialize @@ -29,6 +30,12 @@ module Gitlab @degradations.values end + def sort_degradations! + @degradations = @degradations.sort_by do |_fingerprint, degradation| + SEVERITY_PRIORITIES[degradation.dig(:severity)] + end.to_h + end + private def valid_degradation?(degradation) diff --git a/lib/gitlab/ci/reports/codequality_reports_comparer.rb b/lib/gitlab/ci/reports/codequality_reports_comparer.rb index 10748b8ca02..e34d9675c10 100644 --- a/lib/gitlab/ci/reports/codequality_reports_comparer.rb +++ b/lib/gitlab/ci/reports/codequality_reports_comparer.rb @@ -7,6 +7,11 @@ module Gitlab def initialize(base_report, head_report) @base_report = base_report @head_report = head_report + + unless not_found? + @base_report.sort_degradations! + @head_report.sort_degradations! + end end def success? diff --git a/lib/gitlab/graphql/docs/helper.rb b/lib/gitlab/graphql/docs/helper.rb index e9ff85d9ca9..f4173e26224 100644 --- a/lib/gitlab/graphql/docs/helper.rb +++ b/lib/gitlab/graphql/docs/helper.rb @@ -27,7 +27,10 @@ module Gitlab MD end - def render_name_and_description(object, level = 3) + # Template methods: + # Methods that return chunks of Markdown for insertion into the document + + def render_name_and_description(object, owner: nil, level: 3) content = [] content << "#{'#' * level} `#{object[:name]}`" @@ -35,10 +38,22 @@ module Gitlab if object[:description].present? desc = object[:description].strip desc += '.' unless desc.ends_with?('.') + end + + if object[:is_deprecated] + owner = Array.wrap(owner) + deprecation = schema_deprecation(owner, object[:name]) + content << (deprecation&.original_description || desc) + content << render_deprecation(object, owner, :block) + else content << desc end - content.join("\n\n") + content.compact.join("\n\n") + end + + def render_return_type(query) + "Returns #{render_field_type(query[:type])}.\n" end def sorted_by_name(objects) @@ -47,39 +62,25 @@ module Gitlab objects.sort_by { |o| o[:name] } end - def render_field(field) - row(render_name(field), render_field_type(field[:type]), render_description(field)) + def render_field(field, owner) + render_row( + render_name(field, owner), + render_field_type(field[:type]), + render_description(field, owner, :inline) + ) end - def render_enum_value(value) - row(render_name(value), render_description(value)) + def render_enum_value(enum, value) + render_row(render_name(value, enum[:name]), render_description(value, enum[:name], :inline)) end - def row(*values) - "| #{values.join(' | ')} |" + def render_union_member(member) + "- [`#{member}`](##{member.downcase})" end - def render_name(object) - rendered_name = "`#{object[:name]}`" - rendered_name += ' **{warning-solid}**' if object[:is_deprecated] - rendered_name - end + # QUERIES: - # Returns the object description. If the object has been deprecated, - # the deprecation reason will be returned in place of the description. - def render_description(object) - return object[:description] unless object[:is_deprecated] - - "**Deprecated:** #{object[:deprecation_reason]}" - end - - def render_field_type(type) - "[`#{type[:info]}`](##{type[:name].downcase})" - end - - def render_return_type(query) - "Returns #{render_field_type(query[:type])}.\n" - end + # Methods that return parts of the schema, or related information: # We are ignoring connections and built in types for now, # they should be added when queries are generated. @@ -103,6 +104,83 @@ module Gitlab !enum_type[:name].in?(%w[__DirectiveLocation __TypeKind]) end end + + private # DO NOT CALL THESE METHODS IN TEMPLATES + + # Template methods + + def render_row(*values) + "| #{values.map { |val| val.to_s.squish }.join(' | ')} |" + end + + def render_name(object, owner = nil) + rendered_name = "`#{object[:name]}`" + rendered_name += ' **{warning-solid}**' if object[:is_deprecated] + rendered_name + end + + # Returns the object description. If the object has been deprecated, + # the deprecation reason will be returned in place of the description. + def render_description(object, owner = nil, context = :block) + owner = Array.wrap(owner) + return render_deprecation(object, owner, context) if object[:is_deprecated] + return if object[:description].blank? + + desc = object[:description].strip + desc += '.' unless desc.ends_with?('.') + desc + end + + def render_deprecation(object, owner, context) + deprecation = schema_deprecation(owner, object[:name]) + return deprecation.markdown(context: context) if deprecation + + reason = object[:deprecation_reason] || 'Use of this is deprecated.' + "**Deprecated:** #{reason}" + end + + def render_field_type(type) + "[`#{type[:info]}`](##{type[:name].downcase})" + end + + # Queries + + # returns the deprecation information for a field or argument + # See: Gitlab::Graphql::Deprecation + def schema_deprecation(type_name, field_name) + schema_member(type_name, field_name)&.deprecation + end + + # Return a part of the schema. + # + # This queries the Schema by owner and name to find: + # + # - fields (e.g. `schema_member('Query', 'currentUser')`) + # - arguments (e.g. `schema_member(['Query', 'project], 'fullPath')`) + def schema_member(type_name, field_name) + type_name = Array.wrap(type_name) + if type_name.size == 2 + arg_name = field_name + type_name, field_name = type_name + else + type_name = type_name.first + arg_name = nil + end + + return if type_name.nil? || field_name.nil? + + type = schema.types[type_name] + return unless type && type.kind.fields? + + field = type.fields[field_name] + return field if arg_name.nil? + + args = field.arguments + is_mutation = field.mutation && field.mutation <= ::Mutations::BaseMutation + args = args['input'].type.unwrap.arguments if is_mutation + + args[arg_name] + end end end end diff --git a/lib/gitlab/graphql/docs/renderer.rb b/lib/gitlab/graphql/docs/renderer.rb index 6abd56c89c6..497567f9389 100644 --- a/lib/gitlab/graphql/docs/renderer.rb +++ b/lib/gitlab/graphql/docs/renderer.rb @@ -10,17 +10,20 @@ module Gitlab # It uses graphql-docs helpers and schema parser, more information in https://github.com/gjtorikian/graphql-docs. # # Arguments: - # schema - the GraphQL schema definition. For GitLab should be: GitlabSchema.graphql_definition + # schema - the GraphQL schema definition. For GitLab should be: GitlabSchema # output_dir: The folder where the markdown files will be saved # template: The path of the haml template to be parsed class Renderer include Gitlab::Graphql::Docs::Helper + attr_reader :schema + def initialize(schema, output_dir:, template:) @output_dir = output_dir @template = template @layout = Haml::Engine.new(File.read(template)) - @parsed_schema = GraphQLDocs::Parser.new(schema, {}).parse + @parsed_schema = GraphQLDocs::Parser.new(schema.graphql_definition, {}).parse + @schema = schema end def contents diff --git a/lib/gitlab/graphql/docs/templates/default.md.haml b/lib/gitlab/graphql/docs/templates/default.md.haml index 847f1777b08..fe73297d0d9 100644 --- a/lib/gitlab/graphql/docs/templates/default.md.haml +++ b/lib/gitlab/graphql/docs/templates/default.md.haml @@ -27,7 +27,7 @@ \ - sorted_by_name(queries).each do |query| - = render_name_and_description(query) + = render_name_and_description(query, owner: 'Query') \ = render_return_type(query) - unless query[:arguments].empty? @@ -35,7 +35,7 @@ ~ "| Name | Type | Description |" ~ "| ---- | ---- | ----------- |" - sorted_by_name(query[:arguments]).each do |argument| - = render_field(argument) + = render_field(argument, query[:type][:name]) \ :plain @@ -58,7 +58,7 @@ ~ "| Field | Type | Description |" ~ "| ----- | ---- | ----------- |" - sorted_by_name(type[:fields]).each do |field| - = render_field(field) + = render_field(field, type[:name]) \ :plain @@ -79,7 +79,7 @@ ~ "| Value | Description |" ~ "| ----- | ----------- |" - sorted_by_name(enum[:values]).each do |value| - = render_enum_value(value) + = render_enum_value(enum, value) \ :plain @@ -121,12 +121,12 @@ \ - graphql_union_types.each do |type| - = render_name_and_description(type, 4) + = render_name_and_description(type, level: 4) \ One of: \ - - type[:possible_types].each do |type_name| - ~ "- [`#{type_name}`](##{type_name.downcase})" + - type[:possible_types].each do |member| + = render_union_member(member) \ :plain @@ -134,7 +134,7 @@ \ - graphql_interface_types.each do |type| - = render_name_and_description(type, 4) + = render_name_and_description(type, level: 4) \ Implementations: \ @@ -144,5 +144,5 @@ ~ "| Field | Type | Description |" ~ "| ----- | ---- | ----------- |" - sorted_by_name(type[:fields] + type[:connections]).each do |field| - = render_field(field) + = render_field(field, type[:name]) \ diff --git a/lib/gitlab/quick_actions/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb index b17a0208f95..8ce13db4c03 100644 --- a/lib/gitlab/quick_actions/command_definition.rb +++ b/lib/gitlab/quick_actions/command_definition.rb @@ -56,15 +56,18 @@ module Gitlab end def execute(context, arg) - return unless executable?(context) + return if noop? count_commands_executed_in(context) + return unless available?(context) + execute_block(action_block, context, arg) end def execute_message(context, arg) - return unless executable?(context) + return if noop? + return _('Could not apply %{name} command.') % { name: name } unless available?(context) if execution_message.respond_to?(:call) execute_block(execution_message, context, arg) @@ -101,10 +104,6 @@ module Gitlab private - def executable?(context) - !noop? && available?(context) - end - def count_commands_executed_in(context) return unless context.respond_to?(:commands_executed_count=) diff --git a/lib/tasks/gitlab/graphql.rake b/lib/tasks/gitlab/graphql.rake index 77377a7e0fd..27bba6aa307 100644 --- a/lib/tasks/gitlab/graphql.rake +++ b/lib/tasks/gitlab/graphql.rake @@ -110,7 +110,7 @@ namespace :gitlab do desc 'GitLab | GraphQL | Generate GraphQL docs' task compile_docs: [:environment, :enable_feature_flags] do - renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema.graphql_definition, render_options) + renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema, render_options) renderer.write @@ -119,7 +119,7 @@ namespace :gitlab do desc 'GitLab | GraphQL | Check if GraphQL docs are up to date' task check_docs: [:environment, :enable_feature_flags] do - renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema.graphql_definition, render_options) + renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema, render_options) doc = File.read(Rails.root.join(OUTPUT_DIR, 'index.md')) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 80765e8540e..09bdf1bfa60 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8645,6 +8645,9 @@ msgstr "" msgid "Could not add admins as members" msgstr "" +msgid "Could not apply %{name} command." +msgstr "" + msgid "Could not archive %{design}. Please try again." msgstr "" diff --git a/spec/factories/ci/reports/codequality_degradations.rb b/spec/factories/ci/reports/codequality_degradations.rb index d82157b457a..8b53f2bf46e 100644 --- a/spec/factories/ci/reports/codequality_degradations.rb +++ b/spec/factories/ci/reports/codequality_degradations.rb @@ -95,4 +95,47 @@ FactoryBot.define do }.with_indifferent_access end end + + # TODO: Use this in all other specs and remove the previous numbered factories + # https://gitlab.com/gitlab-org/gitlab/-/issues/325886 + factory :codequality_degradation, class: Hash do + skip_create + + # Feel free to add in more configurable properties here + # as the need arises + fingerprint { SecureRandom.hex } + severity { "major" } + + Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.keys.each do |s| + trait s.to_sym do + severity { s } + end + end + + initialize_with do + { + "categories": [ + "Complexity" + ], + "check_name": "argument_count", + "content": { + "body": "" + }, + "description": "Avoid parameter lists longer than 5 parameters. [12/5]", + "fingerprint": fingerprint, + "location": { + "path": "file_a.rb", + "lines": { + "begin": 10, + "end": 10 + } + }, + "other_locations": [], + "remediation_points": 900000, + "severity": severity, + "type": "issue", + "engine_name": "structure" + }.with_indifferent_access + end + end end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index caa42f77016..e96a60b2ab2 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Dashboard shortcuts', :js do find('body').send_keys([:shift, 'M']) - check_page_title('Merge Requests') + check_page_title('Merge requests') find('body').send_keys([:shift, 'T']) diff --git a/spec/fixtures/emails/update_commands_only.eml b/spec/fixtures/emails/update_commands_only.eml new file mode 100644 index 00000000000..9442d9423f0 --- /dev/null +++ b/spec/fixtures/emails/update_commands_only.eml @@ -0,0 +1,22 @@ +Return-Path: +Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for ; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +Date: Thu, 13 Jun 2013 17:03:48 -0400 +From: Jake the Dog +To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo +Message-ID: +In-Reply-To: +References: +Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Mime-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +X-Sieve: CMU Sieve 2.2 +X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, + 13 Jun 2013 14:03:48 -0700 (PDT) +X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 + +/close diff --git a/spec/graphql/features/feature_flag_spec.rb b/spec/graphql/features/feature_flag_spec.rb index 95a5bcab6f4..30238cf9cb3 100644 --- a/spec/graphql/features/feature_flag_spec.rb +++ b/spec/graphql/features/feature_flag_spec.rb @@ -15,7 +15,6 @@ RSpec.describe 'Graphql Field feature flags' do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end subject { result } diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb index 6388ceb358c..54b59317b55 100644 --- a/spec/graphql/types/base_field_spec.rb +++ b/spec/graphql/types/base_field_spec.rb @@ -128,7 +128,6 @@ RSpec.describe Types::BaseField do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end it 'returns false if the feature is not enabled' do diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb new file mode 100644 index 00000000000..e329968e6c0 --- /dev/null +++ b/spec/helpers/sidebars_helper_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe SidebarsHelper do + describe '#sidebar_tracking_attributes_by_object' do + subject { helper.sidebar_tracking_attributes_by_object(object) } + + before do + allow(helper).to receive(:tracking_enabled?).and_return(true) + end + + context 'when object is a project' do + let(:object) { build(:project) } + + it 'returns tracking attrs for project' do + expect(subject[:data]).to eq({ track_label: 'projects_side_navigation', track_property: 'projects_side_navigation', track_action: 'render' }) + end + end + + context 'when object is a group' do + let(:object) { build(:group) } + + it 'returns tracking attrs for group' do + expect(subject[:data]).to eq({ track_label: 'groups_side_navigation', track_property: 'groups_side_navigation', track_action: 'render' }) + end + end + + context 'when object is a user' do + let(:object) { build(:user) } + + it 'returns tracking attrs for user' do + expect(subject[:data]).to eq({ track_label: 'user_side_navigation', track_property: 'user_side_navigation', track_action: 'render' }) + end + end + + context 'when object is something else' do + let(:object) { build(:ci_pipeline) } + + it 'returns no attributes' do + expect(subject).to eq({}) + end + end + end +end diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb index 007ebac0073..a8547b39b27 100644 --- a/spec/lib/api/helpers_spec.rb +++ b/spec/lib/api/helpers_spec.rb @@ -183,7 +183,6 @@ RSpec.describe API::Helpers do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end context 'with feature enabled' do diff --git a/spec/lib/feature/gitaly_spec.rb b/spec/lib/feature/gitaly_spec.rb index 6a93d3e3240..696427bb8b6 100644 --- a/spec/lib/feature/gitaly_spec.rb +++ b/spec/lib/feature/gitaly_spec.rb @@ -8,7 +8,6 @@ RSpec.describe Feature::Gitaly do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end describe ".enabled?" do diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb index f08f1dda621..3e158391d7f 100644 --- a/spec/lib/feature_spec.rb +++ b/spec/lib/feature_spec.rb @@ -123,35 +123,12 @@ RSpec.describe Feature, stub_feature_flags: false do end describe '.enabled?' do - let(:disabled_ff_definition) do - Feature::Definition.new( - 'development/disabled_feature_flag.yml', - name: 'disabled_feature_flag', - type: 'development', - default_enabled: false - ) + it 'returns false for undefined feature' do + expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey end - let(:enabled_ff_definition) do - Feature::Definition.new( - 'development/enabled_feature_flag.yml', - name: 'enabled_feature_flag', - type: 'development', - default_enabled: true - ) - end - - before do - allow(Feature::Definition).to receive(:definitions) do - { - disabled_ff_definition.key => disabled_ff_definition, - enabled_ff_definition.key => enabled_ff_definition - } - end - end - - it 'raises an exception for undefined feature' do - expect { described_class.enabled?(:some_random_feature_flag) }.to raise_error Feature::InvalidFeatureFlagError + it 'returns true for undefined feature with default_enabled' do + expect(described_class.enabled?(:some_random_feature_flag, default_enabled: true)).to be_truthy end it 'returns false for existing disabled feature in the database' do @@ -169,58 +146,39 @@ RSpec.describe Feature, stub_feature_flags: false do it { expect(described_class.send(:l1_cache_backend)).to eq(Gitlab::ProcessMemoryCache.cache_backend) } it { expect(described_class.send(:l2_cache_backend)).to eq(Rails.cache) } - it 'caches the status in L1 and L2 caches', :request_store, :use_clean_rails_memory_store_caching, :aggregate_failures do + it 'caches the status in L1 and L2 caches', + :request_store, :use_clean_rails_memory_store_caching do described_class.enable(:enabled_feature_flag) - flipper_features_key = 'flipper/v1/features' - flipper_feature_key = 'flipper/v1/feature/enabled_feature_flag' + flipper_key = "flipper/v1/feature/enabled_feature_flag" - allow(described_class.send(:l2_cache_backend)).to receive(:fetch).twice.and_call_original - allow(described_class.send(:l1_cache_backend)).to receive(:fetch).twice.and_call_original + expect(described_class.send(:l2_cache_backend)) + .to receive(:fetch) + .once + .with(flipper_key, expires_in: 1.hour) + .and_call_original + + expect(described_class.send(:l1_cache_backend)) + .to receive(:fetch) + .once + .with(flipper_key, expires_in: 1.minute) + .and_call_original 2.times do expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy end - - expect(described_class.send(:l2_cache_backend)).to have_received(:fetch).with(flipper_features_key, expires_in: 1.hour) - expect(described_class.send(:l2_cache_backend)).to have_received(:fetch).with(flipper_feature_key, expires_in: 1.hour) - - expect(described_class.send(:l1_cache_backend)).to have_received(:fetch).with(flipper_features_key, expires_in: 1.minute) - expect(described_class.send(:l1_cache_backend)).to have_received(:fetch).with(flipper_feature_key, expires_in: 1.minute) end - it 'returns the default value when the database does not exist', :aggregate_falures do - a_feature = Feature::Definition.new( - 'development/a_feature.yml', - name: 'a_feature', - type: 'development', - default_enabled: true - ) - - allow(Feature::Definition).to receive(:definitions) do - { a_feature.key => a_feature } - end - + it 'returns the default value when the database does not exist' do + fake_default = double('fake default') expect(ActiveRecord::Base).to receive(:connection) { raise ActiveRecord::NoDatabaseError, "No database" } - expect(described_class.enabled?(:a_feature)).to eq(true) + expect(described_class.enabled?(:a_feature, default_enabled: fake_default)).to eq(fake_default) end - context 'cached feature flag', :request_store, :use_clean_rails_memory_store_caching, :aggregate_failures do + context 'cached feature flag', :request_store do let(:flag) { :some_feature_flag } - let(:some_feature_flag) do - Feature::Definition.new( - "development/#{flag}.yml", - name: flag.to_s, - type: 'development', - default_enabled: true - ) - end before do - allow(Feature::Definition).to receive(:definitions) do - { some_feature_flag.key => some_feature_flag } - end - described_class.send(:flipper).memoize = false described_class.enabled?(flag) end @@ -315,48 +273,63 @@ RSpec.describe Feature, stub_feature_flags: false do .to raise_error(/The `type:` of/) end - it 'reads the default from the YAML definition' do - expect(described_class.enabled?(:my_feature_flag)).to eq(false) + it 'when invalid default_enabled is used' do + expect { described_class.enabled?(:my_feature_flag, default_enabled: true) } + .to raise_error(/The `default_enabled:` of/) end - context 'when YAML definition does not exist for an optional type' do - let(:optional_type) { described_class::Shared::TYPES.find { |name, attrs| attrs[:optional] }.first } + context 'when `default_enabled: :yaml` is used in code' do + it 'reads the default from the YAML definition' do + expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(false) + end - context 'when in dev or test environment' do - it 'raises an error for dev' do - expect { described_class.enabled?(:non_existent_flag, type: optional_type) } - .to raise_error( - Feature::InvalidFeatureFlagError, - "The feature flag YAML definition for 'non_existent_flag' does not exist") + context 'when default_enabled is true in the YAML definition' do + let(:default_enabled) { true } + + it 'reads the default from the YAML definition' do + expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(true) end end - context 'when in production' do - before do - allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false) - end + context 'when YAML definition does not exist for an optional type' do + let(:optional_type) { described_class::Shared::TYPES.find { |name, attrs| attrs[:optional] }.first } - context 'when database exists' do - before do - allow(Gitlab::Database).to receive(:exists?).and_return(true) - end - - it 'checks the persisted status and returns false' do - expect(described_class).to receive(:get).with(:non_existent_flag).and_call_original - - expect(described_class.enabled?(:non_existent_flag, type: optional_type)).to eq(false) + context 'when in dev or test environment' do + it 'raises an error for dev' do + expect { described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml) } + .to raise_error( + Feature::InvalidFeatureFlagError, + "The feature flag YAML definition for 'non_existent_flag' does not exist") end end - context 'when database does not exist' do + context 'when in production' do before do - allow(Gitlab::Database).to receive(:exists?).and_return(false) + allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false) end - it 'returns false without checking the status in the database' do - expect(described_class).not_to receive(:get) + context 'when database exists' do + before do + allow(Gitlab::Database).to receive(:exists?).and_return(true) + end - expect(described_class.enabled?(:non_existent_flag, type: optional_type)).to eq(false) + it 'checks the persisted status and returns false' do + expect(described_class).to receive(:get).with(:non_existent_flag).and_call_original + + expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false) + end + end + + context 'when database does not exist' do + before do + allow(Gitlab::Database).to receive(:exists?).and_return(false) + end + + it 'returns false without checking the status in the database' do + expect(described_class).not_to receive(:get) + + expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false) + end end end end @@ -364,36 +337,13 @@ RSpec.describe Feature, stub_feature_flags: false do end end - describe '.disabled?' do - let(:disabled_ff_definition) do - Feature::Definition.new( - 'development/disabled_feature_flag.yml', - name: 'disabled_feature_flag', - type: 'development', - default_enabled: false - ) + describe '.disable?' do + it 'returns true for undefined feature' do + expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy end - let(:enabled_ff_definition) do - Feature::Definition.new( - 'development/enabled_feature_flag.yml', - name: 'enabled_feature_flag', - type: 'development', - default_enabled: true - ) - end - - before do - allow(Feature::Definition).to receive(:definitions) do - { - disabled_ff_definition.key => disabled_ff_definition, - enabled_ff_definition.key => enabled_ff_definition - } - end - end - - it 'raises an exception for undefined feature' do - expect { described_class.disabled?(:some_random_feature_flag) }.to raise_error Feature::InvalidFeatureFlagError + it 'returns false for undefined feature with default_enabled' do + expect(described_class.disabled?(:some_random_feature_flag, default_enabled: true)).to be_falsey end it 'returns true for existing disabled feature in the database' do diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb index b322e55cb5a..8378d096fcf 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb @@ -6,15 +6,17 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do let(:comparer) { described_class.new(base_report, head_report) } let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new } let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new } - let(:degradation_1) { build(:codequality_degradation_1) } - let(:degradation_2) { build(:codequality_degradation_2) } + let(:major_degradation) { build(:codequality_degradation, :major) } + let(:minor_degradation) { build(:codequality_degradation, :major) } + let(:critical_degradation) { build(:codequality_degradation, :critical) } + let(:blocker_degradation) { build(:codequality_degradation, :blocker) } describe '#status' do subject(:report_status) { comparer.status } context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns status failed' do @@ -50,7 +52,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns the number of new errors' do @@ -70,8 +72,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has an error and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'counts the base report error as resolved' do @@ -81,7 +83,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors head has no errors' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'counts the base report errors as resolved' do @@ -91,8 +93,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) end it 'returns zero' do @@ -102,7 +104,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns zero' do @@ -124,7 +126,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has an error' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'returns zero' do @@ -134,7 +136,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when head report has an error' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'includes the head report error in the count' do @@ -144,8 +146,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head report has errors' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'includes errors in the count' do @@ -155,9 +157,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head report has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'includes errors in the count' do @@ -179,20 +181,28 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has the same error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + base_report.add_degradation(critical_degradation) + base_report.add_degradation(blocker_degradation) + head_report.add_degradation(critical_degradation) + head_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end - it 'includes the base report errors' do - expect(existing_errors).to contain_exactly(degradation_1) + it 'includes the base report errors sorted by severity' do + expect(existing_errors).to eq([ + blocker_degradation, + critical_degradation, + major_degradation + ]) end end context 'when base report has errors and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'returns an empty array' do @@ -202,7 +212,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns an empty array' do @@ -224,19 +234,25 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has more errors' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(critical_degradation) + head_report.add_degradation(minor_degradation) + head_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) end - it 'includes errors not found in the base report' do - expect(new_errors).to eq([degradation_2]) + it 'includes errors not found in the base report sorted by severity' do + expect(new_errors).to eq([ + blocker_degradation, + critical_degradation, + minor_degradation + ]) end end context 'when base report has an error and head has no errors' do before do - base_report.add_degradation(degradation_1) + base_report.add_degradation(major_degradation) end it 'returns an empty array' do @@ -246,11 +262,11 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns the head report error' do - expect(new_errors).to eq([degradation_1]) + expect(new_errors).to eq([major_degradation]) end end @@ -268,9 +284,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report errors are still found in the head report' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + head_report.add_degradation(major_degradation) + head_report.add_degradation(minor_degradation) end it 'returns an empty array' do @@ -280,18 +296,25 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do context 'when base report has errors and head has a different error' do before do - base_report.add_degradation(degradation_1) - head_report.add_degradation(degradation_2) + base_report.add_degradation(major_degradation) + base_report.add_degradation(minor_degradation) + base_report.add_degradation(critical_degradation) + base_report.add_degradation(blocker_degradation) + head_report.add_degradation(major_degradation) end - it 'returns the base report error' do - expect(resolved_errors).to eq([degradation_1]) + it 'returns the base report errors not found in the head report, sorted by severity' do + expect(resolved_errors).to eq([ + blocker_degradation, + critical_degradation, + minor_degradation + ]) end end context 'when base report does not have errors and head has errors' do before do - head_report.add_degradation(degradation_1) + head_report.add_degradation(major_degradation) end it 'returns an empty array' do diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb index d6d8ace86c5..3b0eaffc54e 100644 --- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb +++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb @@ -77,4 +77,36 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do end end end + + describe '#sort_degradations!' do + let(:major) { build(:codequality_degradation, :major) } + let(:minor) { build(:codequality_degradation, :minor) } + let(:blocker) { build(:codequality_degradation, :blocker) } + let(:info) { build(:codequality_degradation, :info) } + let(:major_2) { build(:codequality_degradation, :major) } + let(:critical) { build(:codequality_degradation, :critical) } + let(:codequality_report) { described_class.new } + + before do + codequality_report.add_degradation(major) + codequality_report.add_degradation(minor) + codequality_report.add_degradation(blocker) + codequality_report.add_degradation(major_2) + codequality_report.add_degradation(info) + codequality_report.add_degradation(critical) + + codequality_report.sort_degradations! + end + + it 'sorts degradations based on severity' do + expect(codequality_report.degradations.values).to eq([ + blocker, + critical, + major, + major_2, + minor, + info + ]) + end + end end diff --git a/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb index 94f28d3399a..d3535fa9bd3 100644 --- a/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteOnIssuableHandler do it_behaves_like :note_handler_shared_examples, true do let_it_be(:recipient) { user } - let(:update_commands_only) { email_reply_fixture('emails/update_commands_only_reply.eml') } + let(:update_commands_only) { email_reply_fixture('emails/update_commands_only.eml') } let(:no_content) { email_reply_fixture('emails/no_content_reply.eml') } let(:commands_in_reply) { email_reply_fixture('emails/commands_in_reply.eml') } let(:with_quick_actions) { email_reply_fixture('emails/valid_reply_with_quick_actions.eml') } diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb index 694e81d6f82..3d3f381b6d2 100644 --- a/spec/lib/gitlab/gon_helper_spec.rb +++ b/spec/lib/gitlab/gon_helper_spec.rb @@ -12,7 +12,6 @@ RSpec.describe Gitlab::GonHelper do describe '#push_frontend_feature_flag' do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end it 'pushes a feature flag to the frontend' do diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb index 5afed8c3390..8c0f7aac081 100644 --- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb +++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb @@ -1,32 +1,35 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' RSpec.describe Gitlab::Graphql::Docs::Renderer do describe '#contents' do - # Returns a Schema that uses the given `type` - def mock_schema(type, field_description) - query_type = Class.new(Types::BaseObject) do - graphql_name 'Query' + let(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/default.md.haml') } - field :foo, type, null: true do - description field_description + let(:query_type) do + Class.new(Types::BaseObject) { graphql_name 'Query' }.tap do |t| + # this keeps type and field_description in scope. + t.field :foo, type, null: true, description: field_description do argument :id, GraphQL::ID_TYPE, required: false, description: 'ID of the object.' end end - - GraphQL::Schema.define( - query: query_type, - resolve_type: ->(obj, ctx) { raise 'Not a real schema' } - ) end - let_it_be(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/default.md.haml') } + let(:mock_schema) do + Class.new(GraphQL::Schema) do + def resolve_type(obj, ctx) + raise 'Not a real schema' + end + end + end + let(:field_description) { 'List of objects.' } subject(:contents) do + mock_schema.query(query_type) + described_class.new( - mock_schema(type, field_description).graphql_definition, + mock_schema, output_dir: nil, template: template ).contents @@ -136,6 +139,22 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description.' + field :foo_with_args, + type: GraphQL::STRING_TYPE, + null: false, + deprecated: { reason: 'Do not use', milestone: '1.10' }, + description: 'A description.' do + argument :fooity, ::GraphQL::INT_TYPE, required: false, description: 'X' + end + field :bar, + type: GraphQL::STRING_TYPE, + null: false, + description: 'A description.', + deprecated: { + reason: :renamed, + milestone: '1.10', + replacement: 'Query.boom' + } end end @@ -145,7 +164,40 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do | Field | Type | Description | | ----- | ---- | ----------- | - | `foo` **{warning-solid}** | [`String!`](#string) | **Deprecated:** This is deprecated. Deprecated in 1.10. | + | `bar` **{warning-solid}** | [`String!`](#string) | **Deprecated** in 1.10. This was renamed. Use: `Query.boom`. | + | `foo` **{warning-solid}** | [`String!`](#string) | **Deprecated** in 1.10. This is deprecated. | + | `fooWithArgs` **{warning-solid}** | [`String!`](#string) | **Deprecated** in 1.10. Do not use. | + DOC + + is_expected.to include(expectation) + end + end + + context 'when a Query.field is deprecated' do + let(:type) { ::GraphQL::INT_TYPE } + + before do + query_type.field( + name: :bar, + type: type, + null: true, + description: 'A bar', + deprecated: { reason: :renamed, milestone: '10.11', replacement: 'Query.foo' } + ) + end + + it 'includes the deprecation' do + expectation = <<~DOC + ### `bar` + + A bar. + + WARNING: + **Deprecated** in 10.11. + This was renamed. + Use: `Query.foo`. + + Returns [`Int`](#int). DOC is_expected.to include(expectation) diff --git a/spec/lib/gitlab/metrics/methods_spec.rb b/spec/lib/gitlab/metrics/methods_spec.rb index 31b67a1e4d3..71135a6e9c5 100644 --- a/spec/lib/gitlab/metrics/methods_spec.rb +++ b/spec/lib/gitlab/metrics/methods_spec.rb @@ -102,11 +102,6 @@ RSpec.describe Gitlab::Metrics::Methods do let(:feature_name) { :some_metric_feature } let(:metric) { call_fetch_metric_method(docstring: docstring, with_feature: feature_name) } - before do - skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check - end - context 'when feature is enabled' do before do stub_feature_flags(feature_name => true) diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index e83adf5bf67..db5a23e2328 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -54,11 +54,6 @@ RSpec.describe Gitlab::Metrics do end describe '.measure' do - before do - skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check - end - context 'without a transaction' do it 'returns the return value of the block' do val = described_class.measure(:foo) { 10 } diff --git a/spec/lib/gitlab/quick_actions/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb index d63c21954f2..73629ce3da2 100644 --- a/spec/lib/gitlab/quick_actions/command_definition_spec.rb +++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb @@ -127,10 +127,10 @@ RSpec.describe Gitlab::QuickActions::CommandDefinition do subject.condition_block = proc { false } end - it "doesn't execute the command" do + it "counts the command as executed" do subject.execute(context, nil) - expect(context.commands_executed_count).to be_nil + expect(context.commands_executed_count).to eq(1) expect(context.run).to be false end end @@ -238,8 +238,8 @@ RSpec.describe Gitlab::QuickActions::CommandDefinition do subject.condition_block = proc { false } end - it 'returns nil' do - expect(subject.execute_message({}, nil)).to be_nil + it 'returns an error message' do + expect(subject.execute_message({}, nil)).to eq('Could not apply command command.') end end diff --git a/spec/models/users/in_product_marketing_email_spec.rb b/spec/models/users/in_product_marketing_email_spec.rb index 8ff365eb1a8..e115faa5a7f 100644 --- a/spec/models/users/in_product_marketing_email_spec.rb +++ b/spec/models/users/in_product_marketing_email_spec.rb @@ -16,28 +16,45 @@ RSpec.describe Users::InProductMarketingEmail, type: :model do it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:track, :series]).with_message('has already been sent') } end - describe '.without_track_or_series' do - let(:track) { 0 } + describe '.without_track_and_series' do + let(:track) { :create } let(:series) { 0 } - let_it_be(:in_product_marketing_email) { create(:in_product_marketing_email, series: 0, track: 0) } + let_it_be(:user) { create(:user) } - subject(:without_track_or_series) { described_class.without_track_or_series(track, series) } + subject(:without_track_and_series) { User.merge(described_class.without_track_and_series(track, series)) } - context 'for the same track and series' do - it { is_expected.to be_empty } + before do + create(:in_product_marketing_email, track: :create, series: 0, user: user) + create(:in_product_marketing_email, track: :create, series: 1, user: user) + create(:in_product_marketing_email, track: :verify, series: 0, user: user) end - context 'for a different track' do - let(:track) { 1 } - - it { is_expected.to eq([in_product_marketing_email])} + context 'when given track and series already exists' do + it { expect(without_track_and_series).to be_empty } end - context 'for a different series' do - let(:series) { 1 } + context 'when track does not exist' do + let(:track) { :trial } - it { is_expected.to eq([in_product_marketing_email])} + it { expect(without_track_and_series).to eq [user] } + end + + context 'when series does not exist' do + let(:series) { 2 } + + it { expect(without_track_and_series).to eq [user] } + end + + context 'when no track or series for a user exists' do + let(:track) { :create } + let(:series) { 0 } + + before do + @other_user = create(:user) + end + + it { expect(without_track_and_series).to eq [@other_user] } end end end diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb index 04c2758f0e9..0e163ec2154 100644 --- a/spec/requests/api/features_spec.rb +++ b/spec/requests/api/features_spec.rb @@ -438,8 +438,6 @@ RSpec.describe API::Features, stub_feature_flags: false do context 'when the gate value was set' do before do - skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check Feature.enable(feature_name) end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 55d17fabc9a..c889867e99b 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -126,6 +126,39 @@ RSpec.describe API::Triggers do end end + describe 'adding arguments to the application context' do + subject { subject_proc.call } + + let(:expected_params) { { client_id: "user/#{user.id}", project: project.full_path } } + let(:subject_proc) { proc { post api("/projects/#{project.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), params: { ref: 'refs/heads/other-branch' } } } + + context 'when triggering a pipeline from a trigger token' do + it_behaves_like 'storing arguments in the application context' + it_behaves_like 'not executing any extra queries for the application context' + end + + context 'when triggered from another running job' do + let!(:trigger) { } + let!(:trigger_request) { } + + context 'when other job is triggered by a user' do + let(:trigger_token) { create(:ci_build, :running, project: project, user: user).token } + + it_behaves_like 'storing arguments in the application context' + it_behaves_like 'not executing any extra queries for the application context' + end + + context 'when other job is triggered by a runner' do + let(:trigger_token) { create(:ci_build, :running, project: project, runner: runner).token } + let(:runner) { create(:ci_runner) } + let(:expected_params) { { client_id: "runner/#{runner.id}", project: project.full_path } } + + it_behaves_like 'storing arguments in the application context' + it_behaves_like 'not executing any extra queries for the application context', 1 + end + end + end + context 'when is triggered by a pipeline hook' do it 'does not create a new pipeline' do expect do diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb index 0399b316b43..d44f179eed8 100644 --- a/spec/requests/api/usage_data_spec.rb +++ b/spec/requests/api/usage_data_spec.rb @@ -73,7 +73,6 @@ RSpec.describe API::UsageData do context 'with unknown event' do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end it 'returns status ok' do @@ -150,7 +149,6 @@ RSpec.describe API::UsageData do context 'with unknown event' do before do skip_feature_flags_yaml_validation - skip_default_enabled_yaml_check end it 'returns status ok' do diff --git a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb index 97696c7df43..3aa4d0b2f39 100644 --- a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb +++ b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @@ -138,38 +138,64 @@ RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute' do it { is_expected.not_to send_in_product_marketing_email } end - context 'when the user has already received any marketing email in this batch' do - before do - other_group = create(:group) - other_group.add_developer(user) - create(:onboarding_progress, namespace: other_group, created_at: previous_action_completed_at, git_write_at: current_action_completed_at) + describe 'do not send emails twice' do + subject { described_class.send_for_all_tracks_and_intervals } + + let(:user) { create(:user, email_opted_in: true) } + + context 'when user already got a specific email' do + before do + create(:in_product_marketing_email, user: user, track: track, series: 0) + end + + it { is_expected.not_to send_in_product_marketing_email(user.id, anything, track, 0) } end - # For any group Notify is called exactly once - it { is_expected.to send_in_product_marketing_email(user.id, anything, :create, 0) } - end + context 'when user already got sent the whole track' do + before do + 0.upto(2) do |series| + create(:in_product_marketing_email, user: user, track: track, series: series) + end + end - context 'when user has already received a specific series in a track before' do - before do - described_class.new(:create, described_class::INTERVAL_DAYS.index(interval)).execute + it 'does not send any of the emails anymore', :aggregate_failures do + 0.upto(2) do |series| + expect(subject).not_to send_in_product_marketing_email(user.id, anything, track, series) + end + end end - # For any group Notify is called exactly once - it { is_expected.to send_in_product_marketing_email(user.id, anything, :create, described_class::INTERVAL_DAYS.index(interval)) } + context 'when user is in two groups' do + let(:other_group) { create(:group) } - context 'when different series' do - let(:interval) { 5 } - let(:actions_completed) { { created_at: frozen_time - 6.days } } + before do + other_group.add_developer(user) + end - it { is_expected.to send_in_product_marketing_email(user.id, anything, :create, described_class::INTERVAL_DAYS.index(interval)) } - end + context 'when both groups would get the same email' do + before do + create(:onboarding_progress, namespace: other_group, **actions_completed) + end - context 'when different track' do - let(:track) { :verify } - let(:interval) { 1 } - let(:actions_completed) { { created_at: frozen_time - 2.days, git_write_at: frozen_time - 2.days } } + it 'does not send the same email twice' do + subject - it { is_expected.to send_in_product_marketing_email(user.id, anything, :verify, described_class::INTERVAL_DAYS.index(interval)) } + expect(Notify).to have_received(:in_product_marketing_email).with(user.id, anything, :create, 0).once + end + end + + context 'when other group gets a different email' do + before do + create(:onboarding_progress, namespace: other_group, created_at: previous_action_completed_at, git_write_at: frozen_time - 2.days) + end + + it 'sends both emails' do + subject + + expect(Notify).to have_received(:in_product_marketing_email).with(user.id, group.id, :create, 0) + expect(Notify).to have_received(:in_product_marketing_email).with(user.id, other_group.id, :verify, 0) + end + end end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index f59749f0b63..d28cb118529 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -345,6 +345,24 @@ RSpec.describe Notes::CreateService do expect(note.errors[:commands_only]).to be_present end + + it 'adds commands failed message to note errors' do + note_text = %(/reopen) + note = described_class.new(project, user, opts.merge(note: note_text)).execute + + expect(note.errors[:commands_only]).to contain_exactly('Could not apply reopen command.') + end + + it 'generates success and failed error messages' do + note_text = %(/close\n/reopen) + service = double(:service) + allow(Issues::UpdateService).to receive(:new).and_return(service) + expect(service).to receive(:execute) + + note = described_class.new(project, user, opts.merge(note: note_text)).execute + + expect(note.errors[:commands_only]).to contain_exactly('Closed this issue. Could not apply reopen command.') + end end end diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index bf35e72a037..01b9a006ccb 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -478,7 +478,7 @@ RSpec.describe QuickActions::InterpretService do end end - shared_examples 'empty command' do |error_msg| + shared_examples 'failed command' do |error_msg| it 'populates {} if content contains an unsupported command' do _, updates, _ = service.execute(content, issuable) @@ -607,10 +607,10 @@ RSpec.describe QuickActions::InterpretService do issuable.update!(confidential: true) end - it 'does not return the success message' do + it 'returns an error message' do _, _, message = service.execute(content, issuable) - expect(message).to be_empty + expect(message).to eq('Could not apply confidential command.') end it 'is not part of the available commands' do @@ -728,7 +728,7 @@ RSpec.describe QuickActions::InterpretService do context 'can not be merged when logged user does not have permissions' do let(:service) { described_class.new(project, create(:user)) } - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply merge command.' do let(:content) { "/merge" } let(:issuable) { merge_request } end @@ -737,7 +737,7 @@ RSpec.describe QuickActions::InterpretService do context 'can not be merged when sha does not match' do let(:service) { described_class.new(project, developer, { merge_request_diff_head_sha: 'othersha' }) } - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply merge command.' do let(:content) { "/merge" } let(:issuable) { merge_request } end @@ -755,21 +755,21 @@ RSpec.describe QuickActions::InterpretService do end context 'issue can not be merged' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply merge command.' do let(:content) { "/merge" } let(:issuable) { issue } end end context 'non persisted merge request cant be merged' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply merge command.' do let(:content) { "/merge" } let(:issuable) { build(:merge_request) } end end context 'not persisted merge request can not be merged' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply merge command.' do let(:content) { "/merge" } let(:issuable) { build(:merge_request, source_project: project) } end @@ -786,7 +786,7 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { merge_request } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/title' } let(:issuable) { issue } end @@ -869,12 +869,12 @@ RSpec.describe QuickActions::InterpretService do end end - it_behaves_like 'empty command', "Failed to assign a user because no user was found." do + it_behaves_like 'failed command', "Failed to assign a user because no user was found." do let(:content) { '/assign @abcd1234' } let(:issuable) { issue } end - it_behaves_like 'empty command', "Failed to assign a user because no user was found." do + it_behaves_like 'failed command', "Failed to assign a user because no user was found." do let(:content) { '/assign' } let(:issuable) { issue } end @@ -890,7 +890,7 @@ RSpec.describe QuickActions::InterpretService do context 'with an issue instead of a merge request' do let(:issuable) { issue } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply assign_reviewer command.' end # CE does not have multiple reviewers @@ -935,7 +935,7 @@ RSpec.describe QuickActions::InterpretService do context 'with an incorrect user' do let(:content) { '/assign_reviewer @abcd1234' } - it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." + it_behaves_like 'failed command', "Failed to assign a reviewer because no user was found." end context 'with the "reviewer" alias' do @@ -953,7 +953,7 @@ RSpec.describe QuickActions::InterpretService do context 'with no user' do let(:content) { '/assign_reviewer' } - it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." + it_behaves_like 'failed command', "Failed to assign a reviewer because no user was found." end context 'includes only the user reference with extra text' do @@ -977,7 +977,7 @@ RSpec.describe QuickActions::InterpretService do context 'with an issue instead of a merge request' do let(:issuable) { issue } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply unassign_reviewer command.' end context 'with anything after the command' do @@ -1035,14 +1035,20 @@ RSpec.describe QuickActions::InterpretService do end end - it_behaves_like 'milestone command' do - let(:content) { "/milestone %#{milestone.title}" } - let(:issuable) { issue } - end + context 'project milestones' do + before do + milestone + end - it_behaves_like 'milestone command' do - let(:content) { "/milestone %#{milestone.title}" } - let(:issuable) { merge_request } + it_behaves_like 'milestone command' do + let(:content) { "/milestone %#{milestone.title}" } + let(:issuable) { issue } + end + + it_behaves_like 'milestone command' do + let(:content) { "/milestone %#{milestone.title}" } + let(:issuable) { merge_request } + end end context 'only group milestones available' do @@ -1181,7 +1187,7 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { merge_request } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply due command.' do let(:content) { '/due 2016-08-28' } let(:issuable) { merge_request } end @@ -1211,7 +1217,7 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { merge_request } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply remove_due_date command.' do let(:content) { '/remove_due_date' } let(:issuable) { merge_request } end @@ -1221,12 +1227,12 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/estimate' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/estimate abc' } let(:issuable) { issue } end @@ -1257,12 +1263,12 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/spend' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/spend abc' } let(:issuable) { issue } end @@ -1323,7 +1329,7 @@ RSpec.describe QuickActions::InterpretService do end context 'if issuable is a Commit' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply todo command.' do let(:issuable) { commit } end end @@ -1379,7 +1385,7 @@ RSpec.describe QuickActions::InterpretService do end end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/copy_metadata' } let(:issuable) { issue } end @@ -1419,19 +1425,19 @@ RSpec.describe QuickActions::InterpretService do end context 'cross project references' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:other_project) { create(:project, :public) } let(:source_issuable) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) } let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { "/copy_metadata imaginary##{non_existing_record_iid}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:other_project) { create(:project, :private) } let(:source_issuable) { create(:issue, project: other_project) } @@ -1448,7 +1454,7 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/duplicate' } let(:issuable) { issue } end @@ -1461,12 +1467,12 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end - it_behaves_like 'empty command', _('Failed to mark this issue as a duplicate because referenced issue was not found.') do + it_behaves_like 'failed command', _('Failed to mark this issue as a duplicate because referenced issue was not found.') do let(:content) { "/duplicate imaginary##{non_existing_record_iid}" } let(:issuable) { issue } end - it_behaves_like 'empty command', _('Failed to mark this issue as a duplicate because referenced issue was not found.') do + it_behaves_like 'failed command', _('Failed to mark this issue as a duplicate because referenced issue was not found.') do let(:other_project) { create(:project, :private) } let(:issue_duplicate) { create(:issue, project: other_project) } @@ -1481,62 +1487,62 @@ RSpec.describe QuickActions::InterpretService do let(:issue) { create(:issue, project: project, author: visitor) } let(:service) { described_class.new(project, visitor) } - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply assign command.' do let(:content) { "/assign @#{developer.username}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply unassign command.' do let(:content) { '/unassign' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply milestone command.' do let(:content) { "/milestone %#{milestone.title}" } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply remove_milestone command.' do let(:content) { '/remove_milestone' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply label command.' do let(:content) { %(/label ~"#{inprogress.title}" ~#{bug.title} ~unknown) } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply unlabel command.' do let(:content) { %(/unlabel ~"#{inprogress.title}") } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply relabel command.' do let(:content) { %(/relabel ~"#{inprogress.title}") } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply due command.' do let(:content) { '/due tomorrow' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply remove_due_date command.' do let(:content) { '/remove_due_date' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply confidential command.' do let(:content) { '/confidential' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply lock command.' do let(:content) { '/lock' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply unlock command.' do let(:content) { '/unlock' } let(:issuable) { issue } end @@ -1554,19 +1560,19 @@ RSpec.describe QuickActions::InterpretService do end context 'ignores command with no argument' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/award' } let(:issuable) { issue } end end context 'ignores non-existing / invalid emojis' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/award noop' } let(:issuable) { issue } end - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/award :lorem_ipsum:' } let(:issuable) { issue } end @@ -1576,7 +1582,7 @@ RSpec.describe QuickActions::InterpretService do let(:content) { '/award :100:' } let(:issuable) { commit } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply award command.' end end @@ -1622,14 +1628,14 @@ RSpec.describe QuickActions::InterpretService do end context 'ignores command with no argument' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply target_branch command.' do let(:content) { '/target_branch' } let(:issuable) { another_merge_request } end end context 'ignores non-existing target branch' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command', 'Could not apply target_branch command.' do let(:content) { '/target_branch totally_non_existing_branch' } let(:issuable) { another_merge_request } end @@ -1697,34 +1703,34 @@ RSpec.describe QuickActions::InterpretService do create(:board, project: project) end - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply board_move command.' end context 'if the given label does not exist' do let(:issuable) { issue } let(:content) { '/board_move ~"Fake Label"' } - it_behaves_like 'empty command', 'Failed to move this issue because label was not found.' + it_behaves_like 'failed command', 'Failed to move this issue because label was not found.' end context 'if multiple labels are given' do let(:issuable) { issue } let(:content) { %{/board_move ~"#{inreview.title}" ~"#{todo.title}"} } - it_behaves_like 'empty command', 'Failed to move this issue because only a single label can be provided.' + it_behaves_like 'failed command', 'Failed to move this issue because only a single label can be provided.' end context 'if the given label is not a list on the board' do let(:issuable) { issue } let(:content) { %{/board_move ~"#{bug.title}"} } - it_behaves_like 'empty command', 'Failed to move this issue because label was not found.' + it_behaves_like 'failed command', 'Failed to move this issue because label was not found.' end context 'if issuable is not an Issue' do let(:issuable) { merge_request } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply board_move command.' end end @@ -1732,7 +1738,7 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { commit } context 'ignores command with no argument' do - it_behaves_like 'empty command' do + it_behaves_like 'failed command' do let(:content) { '/tag' } end end @@ -1797,7 +1803,7 @@ RSpec.describe QuickActions::InterpretService do context 'if issuable is not an Issue' do let(:issuable) { merge_request } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply create_merge_request command.' end context "when logged user cannot create_merge_requests in the project" do @@ -1807,14 +1813,14 @@ RSpec.describe QuickActions::InterpretService do project.add_developer(developer) end - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply create_merge_request command.' end context 'when logged user cannot push code to the project' do let(:project) { create(:project, :private) } let(:service) { described_class.new(project, create(:user)) } - it_behaves_like 'empty command' + it_behaves_like 'failed command', 'Could not apply create_merge_request command.' end it 'populates create_merge_request with branch_name and issue iid' do @@ -1953,7 +1959,7 @@ RSpec.describe QuickActions::InterpretService do context 'invite_email command' do let_it_be(:issuable) { issue } - it_behaves_like 'empty command', "No email participants were added. Either none were provided, or they already exist." do + it_behaves_like 'failed command', "No email participants were added. Either none were provided, or they already exist." do let(:content) { '/invite_email' } end @@ -1964,7 +1970,7 @@ RSpec.describe QuickActions::InterpretService do issuable.issue_email_participants.create!(email: "a@gitlab.com") end - it_behaves_like 'empty command', "No email participants were added. Either none were provided, or they already exist." + it_behaves_like 'failed command', "No email participants were added. Either none were provided, or they already exist." end context 'with new email participants' do diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb index 9dffea7c94e..70e2a11d202 100644 --- a/spec/support/shared_contexts/email_shared_context.rb +++ b/spec/support/shared_contexts/email_shared_context.rb @@ -82,8 +82,8 @@ RSpec.shared_examples :note_handler_shared_examples do |forwardable| let!(:email_raw) { update_commands_only } context 'and current user cannot update noteable' do - it 'raises a CommandsOnlyNoteError' do - expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidNoteError) + it 'does not raise an error' do + expect { receiver.execute }.not_to raise_error end end @@ -92,15 +92,11 @@ RSpec.shared_examples :note_handler_shared_examples do |forwardable| project.add_developer(user) end - it 'does not raise an error', unless: forwardable do + it 'does not raise an error' do expect { receiver.execute }.to change { noteable.resource_state_events.count }.by(1) expect(noteable.reload).to be_closed end - - it 'raises an InvalidNoteError', if: forwardable do - expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidNoteError) - end end end end diff --git a/spec/support/shared_examples/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/nav_sidebar_shared_examples.rb index e084a957785..3e500683712 100644 --- a/spec/support/shared_examples/nav_sidebar_shared_examples.rb +++ b/spec/support/shared_examples/nav_sidebar_shared_examples.rb @@ -24,3 +24,13 @@ RSpec.shared_examples 'page has active sub tab' do |title| .to have_content(title) end end + +RSpec.shared_examples 'sidebar includes snowplow attributes' do |track_action, track_label, track_property| + specify do + allow(view).to receive(:tracking_enabled?).and_return(true) + + render + + expect(rendered).to have_css(".nav-sidebar[data-track-action=\"#{track_action}\"][data-track-label=\"#{track_label}\"][data-track-property=\"#{track_property}\"]") + end +end diff --git a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb index b3c8450fb48..640f463b45d 100644 --- a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb @@ -10,4 +10,5 @@ RSpec.describe 'layouts/nav/sidebar/_group' do end it_behaves_like 'has nav sidebar' + it_behaves_like 'sidebar includes snowplow attributes', 'render', 'groups_side_navigation', 'groups_side_navigation' end diff --git a/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb index 0f6dcf8e57f..3d28be68b25 100644 --- a/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb @@ -10,4 +10,5 @@ RSpec.describe 'layouts/nav/sidebar/_profile' do end it_behaves_like 'has nav sidebar' + it_behaves_like 'sidebar includes snowplow attributes', 'render', 'user_side_navigation', 'user_side_navigation' end diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index 8951f358b7a..18467f95380 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -346,4 +346,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do end end end + + it_behaves_like 'sidebar includes snowplow attributes', 'render', 'projects_side_navigation', 'projects_side_navigation' end