diff options
84 files changed, 1279 insertions, 443 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index e747e7a4858..ffdd0ef10e3 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -1281,7 +1281,6 @@ lib/gitlab/checks/** @proglottis @toon /app/workers/stuck_ci_jobs_worker.rb /app/workers/update_external_pull_requests_worker.rb /lib/api/commit_statuses.rb -/ee/app/models/merge_train.rb /ee/app/finders/merge_trains_finder.rb /ee/app/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service.rb /ee/app/services/auto_merge/merge_train_service.rb @@ -1290,7 +1289,6 @@ lib/gitlab/checks/** @proglottis @toon /ee/app/controllers/ee/projects/pipelines_controller.rb /ee/app/controllers/projects/pipelines/ /ee/app/controllers/projects/subscriptions_controller.rb -/ee/app/models/merge_train.rb /ee/app/helpers/ee/projects/pipeline_helper.rb /ee/app/views/ci_minutes_usage_mailer/ /ee/app/views/projects/pipelines/ @@ -1334,7 +1332,6 @@ lib/gitlab/checks/** @proglottis @toon /spec/workers/run_pipeline_schedule_worker_spec.rb /spec/workers/stuck_ci_jobs_worker_spec.rb /spec/workers/update_external_pull_requests_worker_spec.rb -/ee/spec/models/merge_train_spec.rb /ee/spec/finders/merge_trains_finder_spec.rb /ee/spec/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service_spec.rb /ee/spec/services/auto_merge/merge_train_service_spec.rb diff --git a/.rubocop_todo/fips/sha1.yml b/.rubocop_todo/fips/sha1.yml index a9637295cab..f39ca78fd81 100644 --- a/.rubocop_todo/fips/sha1.yml +++ b/.rubocop_todo/fips/sha1.yml @@ -30,7 +30,6 @@ Fips/SHA1: - 'ee/spec/lib/gitlab/ci/reports/security/locations/dast_spec.rb' - 'ee/spec/lib/gitlab/ci/reports/security/locations/dependency_scanning_spec.rb' - 'ee/spec/migrations/update_vulnerability_occurrences_location_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/models/resource_weight_event_spec.rb' - 'ee/spec/models/vulnerabilities/finding_signature_spec.rb' - 'ee/spec/models/vulnerabilities/finding_spec.rb' diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml index a35783ac898..7de96b82445 100644 --- a/.rubocop_todo/gitlab/namespaced_class.yml +++ b/.rubocop_todo/gitlab/namespaced_class.yml @@ -923,7 +923,6 @@ Gitlab/NamespacedClass: - 'ee/app/models/license.rb' - 'ee/app/models/merge_request_block.rb' - 'ee/app/models/merge_request_diff_detail.rb' - - 'ee/app/models/merge_train.rb' - 'ee/app/models/namespace_limit.rb' - 'ee/app/models/path_lock.rb' - 'ee/app/models/productivity_analytics.rb' diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml index af8f5a62980..289b732e958 100644 --- a/.rubocop_todo/layout/argument_alignment.yml +++ b/.rubocop_todo/layout/argument_alignment.yml @@ -1338,14 +1338,6 @@ Layout/ArgumentAlignment: - 'ee/spec/graphql/types/pipeline_security_report_finding_type_spec.rb' - 'ee/spec/graphql/types/project_type_spec.rb' - 'ee/spec/graphql/types/vulnerability_type_spec.rb' - - 'ee/spec/helpers/billing_plans_helper_spec.rb' - - 'ee/spec/helpers/ee/integrations_helper_spec.rb' - - 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb' - - 'ee/spec/helpers/ee/namespaces_helper_spec.rb' - - 'ee/spec/helpers/ee/trial_registration_helper_spec.rb' - - 'ee/spec/helpers/license_monitoring_helper_spec.rb' - - 'ee/spec/helpers/projects_helper_spec.rb' - - 'ee/spec/helpers/vulnerabilities_helper_spec.rb' - 'ee/spec/lib/analytics/group_activity_calculator_spec.rb' - 'ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb' - 'ee/spec/lib/api/entities/protected_environments/approval_rule_for_summary_spec.rb' @@ -1640,7 +1632,6 @@ Layout/ArgumentAlignment: - 'ee/spec/services/vulnerabilities/user_notes_count_service_spec.rb' - 'ee/spec/services/vulnerability_feedback/create_service_spec.rb' - 'ee/spec/services/vulnerability_merge_request_links/create_service_spec.rb' - - 'ee/spec/support/helpers/vulnerability_helpers.rb' - 'ee/spec/support/shared_examples/audit/audit_event_type_stream_shared_examples.rb' - 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb' - 'ee/spec/support/shared_examples/features/credentials_inventory_shared_examples.rb' @@ -2173,18 +2164,6 @@ Layout/ArgumentAlignment: - 'spec/graphql/types/project_type_spec.rb' - 'spec/graphql/types/root_storage_statistics_type_spec.rb' - 'spec/graphql/types/todo_type_spec.rb' - - 'spec/helpers/avatars_helper_spec.rb' - - 'spec/helpers/emoji_helper_spec.rb' - - 'spec/helpers/feature_flags_helper_spec.rb' - - 'spec/helpers/namespaces_helper_spec.rb' - - 'spec/helpers/notify_helper_spec.rb' - - 'spec/helpers/page_layout_helper_spec.rb' - - 'spec/helpers/routing/pseudonymization_helper_spec.rb' - - 'spec/helpers/storage_helper_spec.rb' - - 'spec/helpers/todos_helper_spec.rb' - - 'spec/helpers/users/callouts_helper_spec.rb' - - 'spec/helpers/users/group_callouts_helper_spec.rb' - - 'spec/helpers/visibility_level_helper_spec.rb' - 'spec/initializers/00_rails_disable_joins_spec.rb' - 'spec/initializers/secret_token_spec.rb' - 'spec/lib/api/every_api_endpoint_spec.rb' @@ -2756,13 +2735,6 @@ Layout/ArgumentAlignment: - 'spec/services/work_items/task_list_reference_removal_service_spec.rb' - 'spec/services/work_items/widgets/description_service/update_service_spec.rb' - 'spec/sidekiq/cron/job_gem_dependency_spec.rb' - - 'spec/support/helpers/api_internal_base_helpers.rb' - - 'spec/support/helpers/board_helpers.rb' - - 'spec/support/helpers/ci/source_pipeline_helpers.rb' - - 'spec/support/helpers/feature_flag_helpers.rb' - - 'spec/support/helpers/graphql_helpers.rb' - - 'spec/support/helpers/stub_object_storage.rb' - - 'spec/support/helpers/workhorse_helpers.rb' - 'spec/support/import_export/export_file_helper.rb' - 'spec/support/redis/redis_shared_examples.rb' - 'spec/support/shared_contexts/bulk_imports_requests_shared_context.rb' diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 86faf32e8be..2499b2ee194 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -2117,7 +2117,6 @@ Layout/LineLength: - 'ee/spec/models/merge_request_spec.rb' - 'ee/spec/models/merge_requests/compliance_violation_spec.rb' - 'ee/spec/models/merge_requests/external_status_check_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/models/namespace_setting_spec.rb' - 'ee/spec/models/note_spec.rb' - 'ee/spec/models/packages/package_file_spec.rb' diff --git a/.rubocop_todo/layout/space_in_lambda_literal.yml b/.rubocop_todo/layout/space_in_lambda_literal.yml index c311f562e93..144b4eb04a7 100644 --- a/.rubocop_todo/layout/space_in_lambda_literal.yml +++ b/.rubocop_todo/layout/space_in_lambda_literal.yml @@ -223,7 +223,6 @@ Layout/SpaceInLambdaLiteral: - 'ee/app/models/iterations/cadence.rb' - 'ee/app/models/merge_request_block.rb' - 'ee/app/models/merge_requests/compliance_violation.rb' - - 'ee/app/models/merge_train.rb' - 'ee/app/models/namespaces/namespace_ban.rb' - 'ee/app/models/requirements_management/requirement.rb' - 'ee/app/models/resource_iteration_event.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index 9e2061049a5..560c99e3b1d 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -499,7 +499,6 @@ RSpec/ContextWording: - 'ee/spec/models/member_spec.rb' - 'ee/spec/models/merge_request/blocking_spec.rb' - 'ee/spec/models/merge_request_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/models/namespace_setting_spec.rb' - 'ee/spec/models/note_spec.rb' - 'ee/spec/models/packages/package_file_spec.rb' diff --git a/.rubocop_todo/rspec/described_class.yml b/.rubocop_todo/rspec/described_class.yml index 94c44a144bf..99800ab8257 100644 --- a/.rubocop_todo/rspec/described_class.yml +++ b/.rubocop_todo/rspec/described_class.yml @@ -26,7 +26,6 @@ RSpec/DescribedClass: - 'ee/spec/models/issue_spec.rb' - 'ee/spec/models/iteration_spec.rb' - 'ee/spec/models/license_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/models/project_import_state_spec.rb' - 'ee/spec/models/release_highlight_spec.rb' - 'ee/spec/models/requirements_management/test_report_spec.rb' diff --git a/.rubocop_todo/rspec/hooks_before_examples.yml b/.rubocop_todo/rspec/hooks_before_examples.yml index a6291452db8..e161a2aba33 100644 --- a/.rubocop_todo/rspec/hooks_before_examples.yml +++ b/.rubocop_todo/rspec/hooks_before_examples.yml @@ -17,7 +17,6 @@ RSpec/HooksBeforeExamples: - 'ee/spec/lib/ee/gitlab/usage_data_counters/hll_redis_counter_spec.rb' - 'ee/spec/lib/gitlab/analytics/cycle_analytics/summary/group/stage_summary_spec.rb' - 'ee/spec/models/ee/merge_request_diff_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/requests/api/boards_spec.rb' - 'ee/spec/requests/ee/projects/deploy_tokens_controller_spec.rb' - 'ee/spec/services/ee/groups/deploy_tokens/create_service_spec.rb' diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml index dfcc1e48ddd..e9ede007d47 100644 --- a/.rubocop_todo/rspec/missing_feature_category.yml +++ b/.rubocop_todo/rspec/missing_feature_category.yml @@ -1254,7 +1254,6 @@ RSpec/MissingFeatureCategory: - 'ee/spec/models/merge_requests/compliance_violation_spec.rb' - 'ee/spec/models/merge_requests/external_status_check_spec.rb' - 'ee/spec/models/merge_requests/status_check_response_spec.rb' - - 'ee/spec/models/merge_train_spec.rb' - 'ee/spec/models/milestone_release_spec.rb' - 'ee/spec/models/milestone_spec.rb' - 'ee/spec/models/namespace_limit_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 01e42c9eaab..24b9b695c69 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -d7ad67347247776ec267d4f2056e2c4cffcf4ebd +c31b9fed97bb01a1790496386ceab8e31e76b1d8 diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue index 2e386fd786e..ff7982319e7 100644 --- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue +++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue @@ -140,6 +140,19 @@ export default { this.infiniteScrollingTriggered = false; this.filterSearchTriggered = true; + // all filters have been cleared reset query param + // and refetch jobs/count with defaults + if (!filters.length) { + updateHistory({ + url: setUrlParams({ statuses: null }, window.location.href, true), + }); + + this.$apollo.queries.jobs.refetch({ statuses: null }); + this.$apollo.queries.jobsCount.refetch({ statuses: null }); + + return; + } + // Eventually there will be more tokens available // this code is written to scale for those tokens filters.forEach((filter) => { diff --git a/app/assets/javascripts/oauth_application/components/oauth_secret.vue b/app/assets/javascripts/oauth_application/components/oauth_secret.vue new file mode 100644 index 00000000000..fabda19c27b --- /dev/null +++ b/app/assets/javascripts/oauth_application/components/oauth_secret.vue @@ -0,0 +1,106 @@ +<script> +import { GlButton, GlModal } from '@gitlab/ui'; +import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/alert'; +import axios from '~/lib/utils/axios_utils'; +import InputCopyToggleVisibility from '~/vue_shared/components/form/input_copy_toggle_visibility.vue'; +import { + CONFIRM_MODAL, + CONFIRM_MODAL_TITLE, + COPY_SECRET, + DESCRIPTION_SECRET, + RENEW_SECRET, + RENEW_SECRET_FAILURE, + RENEW_SECRET_SUCCESS, + WARNING_NO_SECRET, +} from '../constants'; + +export default { + CONFIRM_MODAL, + CONFIRM_MODAL_TITLE, + COPY_SECRET, + DESCRIPTION_SECRET, + RENEW_SECRET, + name: 'OAuthSecret', + components: { + GlButton, + GlModal, + InputCopyToggleVisibility, + }, + inject: ['initialSecret', 'renewPath'], + data() { + return { + secret: this.initialSecret, + alert: null, + isModalVisible: false, + isLoading: false, + }; + }, + computed: { + actionPrimary() { + return { + text: this.$options.RENEW_SECRET, + attributes: { + variant: 'confirm', + loading: this.isLoading, + }, + }; + }, + }, + created() { + if (!this.secret) { + this.alert = createAlert({ message: WARNING_NO_SECRET, variant: VARIANT_WARNING }); + } + }, + methods: { + displayModal() { + this.isModalVisible = true; + }, + async renewSecret(event) { + event.preventDefault(); + this.isLoading = true; + this.alert?.dismiss(); + + try { + const { data } = await axios.put(this.renewPath); + this.alert = createAlert({ message: RENEW_SECRET_SUCCESS, variant: VARIANT_SUCCESS }); + this.secret = data.secret; + } catch { + this.alert = createAlert({ message: RENEW_SECRET_FAILURE }); + } finally { + this.isLoading = false; + this.isModalVisible = false; + } + }, + }, +}; +</script> + +<template> + <div class="gl-display-flex gl-flex-wrap-wrap gl-gap-5"> + <input-copy-toggle-visibility + v-if="secret" + :copy-button-title="$options.COPY_SECRET" + :value="secret" + class="gl-mt-n3 gl-mb-0" + > + <template #description> + {{ $options.DESCRIPTION_SECRET }} + </template> + </input-copy-toggle-visibility> + + <gl-button category="secondary" class="gl-align-self-start" @click="displayModal">{{ + $options.RENEW_SECRET + }}</gl-button> + + <gl-modal + v-model="isModalVisible" + :title="$options.CONFIRM_MODAL_TITLE" + size="sm" + modal-id="modal-renew-secret" + :action-primary="actionPrimary" + @primary="renewSecret" + > + {{ $options.CONFIRM_MODAL }} + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/oauth_application/constants.js b/app/assets/javascripts/oauth_application/constants.js new file mode 100644 index 00000000000..5eaacadda78 --- /dev/null +++ b/app/assets/javascripts/oauth_application/constants.js @@ -0,0 +1,20 @@ +import { __, s__ } from '~/locale'; + +export const CONFIRM_MODAL = s__( + 'AuthorizedApplication|Are you sure you want to renew this secret? Any applications using the old secret will no longer be able to authenticate with GitLab.', +); +export const CONFIRM_MODAL_TITLE = s__('AuthorizedApplication|Renew secret?'); +export const COPY_SECRET = __('Copy secret'); +export const DESCRIPTION_SECRET = __( + 'This is the only time the secret is accessible. Copy the secret and store it securely.', +); +export const RENEW_SECRET = s__('AuthorizedApplication|Renew secret'); +export const RENEW_SECRET_FAILURE = s__( + 'AuthorizedApplication|There was an error trying to renew the application secret. Please try again.', +); +export const RENEW_SECRET_SUCCESS = s__( + 'AuthorizedApplication|Application secret was successfully renewed.', +); +export const WARNING_NO_SECRET = __( + 'The secret is only available when you create the application or renew the secret.', +); diff --git a/app/assets/javascripts/oauth_application/index.js b/app/assets/javascripts/oauth_application/index.js new file mode 100644 index 00000000000..f8f1f647a15 --- /dev/null +++ b/app/assets/javascripts/oauth_application/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import OAuthSecret from './components/oauth_secret.vue'; + +export const initOAuthApplicationSecret = () => { + const el = document.querySelector('#js-oauth-application-secret'); + + if (!el) { + return null; + } + + const { initialSecret, renewPath } = el.dataset; + + return new Vue({ + el, + name: 'OAuthSecretRoot', + provide: { initialSecret, renewPath }, + render(h) { + return h(OAuthSecret); + }, + }); +}; diff --git a/app/assets/javascripts/pages/admin/applications/index.js b/app/assets/javascripts/pages/admin/applications/index.js index 3397b02aeba..df9e38431b0 100644 --- a/app/assets/javascripts/pages/admin/applications/index.js +++ b/app/assets/javascripts/pages/admin/applications/index.js @@ -1,3 +1,5 @@ import initApplicationDeleteButtons from '~/admin/applications'; +import { initOAuthApplicationSecret } from '~/oauth_application'; initApplicationDeleteButtons(); +initOAuthApplicationSecret(); diff --git a/app/assets/javascripts/pages/groups/settings/applications/index.js b/app/assets/javascripts/pages/groups/settings/applications/index.js new file mode 100644 index 00000000000..4dee5433ec9 --- /dev/null +++ b/app/assets/javascripts/pages/groups/settings/applications/index.js @@ -0,0 +1,3 @@ +import { initOAuthApplicationSecret } from '~/oauth_application'; + +initOAuthApplicationSecret(); diff --git a/app/assets/javascripts/pages/oauth/applications/index.js b/app/assets/javascripts/pages/oauth/applications/index.js new file mode 100644 index 00000000000..4dee5433ec9 --- /dev/null +++ b/app/assets/javascripts/pages/oauth/applications/index.js @@ -0,0 +1,3 @@ +import { initOAuthApplicationSecret } from '~/oauth_application'; + +initOAuthApplicationSecret(); diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 76564981c9b..d97fcc5df74 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -47,10 +47,9 @@ class Admin::ApplicationsController < Admin::ApplicationController @application.renew_secret if @application.save - flash.now[:notice] = s_('AuthorizedApplication|Application secret was successfully updated.') - render :show + render json: { secret: @application.plaintext_secret } else - redirect_to admin_application_url(@application) + render json: { errors: @application.errors }, status: :unprocessable_entity end end diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb index 2bf5c95937b..3ae1ae824a0 100644 --- a/app/controllers/groups/settings/applications_controller.rb +++ b/app/controllers/groups/settings/applications_controller.rb @@ -46,10 +46,9 @@ module Groups @application.renew_secret if @application.save - flash.now[:notice] = s_('AuthorizedApplication|Application secret was successfully updated.') - render :show + render json: { secret: @application.plaintext_secret } else - redirect_to group_settings_application_url(@group, @application) + render json: { errors: @application.errors }, status: :unprocessable_entity end end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 7a31738188a..2d5421f9f74 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -45,10 +45,9 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController @application.renew_secret if @application.save - flash.now[:notice] = s_('AuthorizedApplication|Application secret was successfully updated.') - render :show + render json: { secret: @application.plaintext_secret } else - redirect_to oauth_application_url(@application) + render json: { errors: @application.errors }, status: :unprocessable_entity end end diff --git a/app/graphql/types/ci/catalog/resource_type.rb b/app/graphql/types/ci/catalog/resource_type.rb new file mode 100644 index 00000000000..b5947826fa1 --- /dev/null +++ b/app/graphql/types/ci/catalog/resource_type.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Types + module Ci + module Catalog + # rubocop: disable Graphql/AuthorizeTypes + class ResourceType < BaseObject + graphql_name 'CiCatalogResource' + + connection_type_class(Types::CountableConnectionType) + + field :id, GraphQL::Types::ID, null: false, description: 'ID of the catalog resource.', + alpha: { milestone: '15.11' } + + field :name, GraphQL::Types::String, null: true, description: 'Name of the catalog resource.', + alpha: { milestone: '15.11' } + + field :description, GraphQL::Types::String, null: true, description: 'Description of the catalog resource.', + alpha: { milestone: '15.11' } + + field :icon, GraphQL::Types::String, null: true, description: 'Icon for the catalog resource.', + method: :avatar_path, alpha: { milestone: '15.11' } + end + # rubocop: enable Graphql/AuthorizeTypes + end + end +end diff --git a/app/models/ci/catalog/resource.rb b/app/models/ci/catalog/resource.rb index 40ec41de6fe..837f1352b4d 100644 --- a/app/models/ci/catalog/resource.rb +++ b/app/models/ci/catalog/resource.rb @@ -13,6 +13,8 @@ module Ci belongs_to :project scope :for_projects, ->(project_ids) { where(project_id: project_ids) } + + delegate :avatar_path, :description, :name, to: :project end end end diff --git a/app/views/shared/doorkeeper/applications/_show.html.haml b/app/views/shared/doorkeeper/applications/_show.html.haml index 19f4c971c1d..6bebbe94a55 100644 --- a/app/views/shared/doorkeeper/applications/_show.html.haml +++ b/app/views/shared/doorkeeper/applications/_show.html.haml @@ -15,16 +15,7 @@ %td = _('Secret') %td - - if @application.plaintext_secret - = render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-5'}) do |c| - = c.body do - = _('This is the only time the secret is accessible. Copy the secret and store it securely.') - = clipboard_button(clipboard_text: @application.plaintext_secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button") - - else - = render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-5'}) do |c| - = c.body do - = _('The secret is only available when you create the application or renew the secret.') - = render 'shared/doorkeeper/applications/update_form', path: renew_path + #js-oauth-application-secret{ data: { initial_secret: @application.plaintext_secret, renew_path: renew_path } } %tr %td diff --git a/app/views/shared/doorkeeper/applications/_update_form.html.haml b/app/views/shared/doorkeeper/applications/_update_form.html.haml deleted file mode 100644 index 1bee3288639..00000000000 --- a/app/views/shared/doorkeeper/applications/_update_form.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- path = local_assigns.fetch(:path) -= form_for(@application, url: path, html: {class: 'gl-display-inline-block', method: "put"}) do |f| - = submit_tag s_('AuthorizedApplication|Renew secret'), data: { confirm: s_("AuthorizedApplication|Are you sure you want to renew this secret? Any applications using the old secret will no longer be able to authenticate with GitLab."), confirm_btn_variant: "danger" }, aria: { label: s_('AuthorizedApplication|Renew secret') }, class: 'gl-button btn btn-md btn-default' diff --git a/db/docs/merge_trains.yml b/db/docs/merge_trains.yml index 3b666322d3b..7d0310236e9 100644 --- a/db/docs/merge_trains.yml +++ b/db/docs/merge_trains.yml @@ -1,10 +1,10 @@ --- table_name: merge_trains classes: -- MergeTrain +- MergeTrains::Car feature_categories: - continuous_integration -description: TODO +description: Each record represents a single merge request which is or was part of a merge train. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/827fc3ccb9335aa29fba0fc532b70015ec4c5186 milestone: '11.11' gitlab_schema: gitlab_main diff --git a/db/docs/schema_inconsistencies.yml b/db/docs/schema_inconsistencies.yml index 85a2681a523..af95f26accb 100644 --- a/db/docs/schema_inconsistencies.yml +++ b/db/docs/schema_inconsistencies.yml @@ -5,4 +5,6 @@ feature_categories: description: The schema_inconsistencies table contains a list of database schema inconsistencies. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114876 milestone: '15.11' +classes: +- Gitlab::Database::SchemaValidation::SchemaInconsistency gitlab_schema: gitlab_main diff --git a/db/post_migrate/20230329091107_truncate_p_ci_runner_machine_builds.rb b/db/post_migrate/20230329091107_truncate_p_ci_runner_machine_builds.rb new file mode 100644 index 00000000000..b9efdd8234d --- /dev/null +++ b/db/post_migrate/20230329091107_truncate_p_ci_runner_machine_builds.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class TruncatePCiRunnerMachineBuilds < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + return unless Gitlab::Database.gitlab_schemas_for_connection(connection).include?(:gitlab_ci) + + execute('TRUNCATE TABLE p_ci_runner_machine_builds') + end + + # no-op + def down; end +end diff --git a/db/post_migrate/20230329091300_swap_ci_runner_machine_builds_primary_key_v2.rb b/db/post_migrate/20230329091300_swap_ci_runner_machine_builds_primary_key_v2.rb new file mode 100644 index 00000000000..ad40ed118b1 --- /dev/null +++ b/db/post_migrate/20230329091300_swap_ci_runner_machine_builds_primary_key_v2.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class SwapCiRunnerMachineBuildsPrimaryKeyV2 < Gitlab::Database::Migration[2.1] + include Gitlab::Database::PartitioningMigrationHelpers + + disable_ddl_transaction! + + TABLE_NAME = :p_ci_runner_machine_builds + BUILDS_TABLE = :ci_builds + + def up + reorder_primary_key_columns([:build_id, :partition_id]) + end + + def down + reorder_primary_key_columns([:partition_id, :build_id]) + end + + private + + def reorder_primary_key_columns(columns) + with_lock_retries(raise_on_exhaustion: true) do + connection.execute(<<~SQL) + LOCK TABLE #{BUILDS_TABLE}, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE; + SQL + + partitions = Gitlab::Database::PostgresPartitionedTable.each_partition(TABLE_NAME).to_a + partitions.each { |partition| drop_table partition.identifier } + + execute <<~SQL + ALTER TABLE #{TABLE_NAME} + DROP CONSTRAINT p_ci_runner_machine_builds_pkey CASCADE; + + ALTER TABLE #{TABLE_NAME} + ADD PRIMARY KEY (#{columns.join(', ')}); + SQL + + partitions.each do |partition| + connection.execute(<<~SQL) + CREATE TABLE IF NOT EXISTS #{partition.identifier} + PARTITION OF #{partition.parent_identifier} #{partition.condition}; + SQL + end + end + end +end diff --git a/db/schema_migrations/20230329091107 b/db/schema_migrations/20230329091107 new file mode 100644 index 00000000000..658d90e662f --- /dev/null +++ b/db/schema_migrations/20230329091107 @@ -0,0 +1 @@ +4ca98e9c93245a8fc1f4124d00d47d73d12b961affde1d53b7262ffc93582d83
\ No newline at end of file diff --git a/db/schema_migrations/20230329091300 b/db/schema_migrations/20230329091300 new file mode 100644 index 00000000000..4a4be644126 --- /dev/null +++ b/db/schema_migrations/20230329091300 @@ -0,0 +1 @@ +a85e3139d843295e666867129575818f61983a8b16eaa73f9b470e394d9c5476
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 06e4541147b..6c71c7d17ce 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -27376,7 +27376,7 @@ ALTER TABLE ONLY operations_user_lists ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id); ALTER TABLE ONLY p_ci_runner_machine_builds - ADD CONSTRAINT p_ci_runner_machine_builds_pkey PRIMARY KEY (partition_id, build_id); + ADD CONSTRAINT p_ci_runner_machine_builds_pkey PRIMARY KEY (build_id, partition_id); ALTER TABLE ONLY packages_build_infos ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 916c01958ca..958436b3c8a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -55,6 +55,26 @@ CI related settings that apply to the entire instance. Returns [`CiApplicationSettings`](#ciapplicationsettings). +### `Query.ciCatalogResources` + +CI Catalog resources visible to the current user. + +WARNING: +**Introduced** in 15.11. +This feature is in Alpha. It can be changed or removed at any time. + +Returns [`CiCatalogResourceConnection`](#cicatalogresourceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="querycicatalogresourcesprojectpath"></a>`projectPath` | [`ID`](#id) | Project with the namespace catalog. | + ### `Query.ciConfig` Linted and processed contents of a CI config. @@ -7114,6 +7134,30 @@ The edge type for [`CiBuildNeed`](#cibuildneed). | <a id="cibuildneededgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | | <a id="cibuildneededgenode"></a>`node` | [`CiBuildNeed`](#cibuildneed) | The item at the end of the edge. | +#### `CiCatalogResourceConnection` + +The connection type for [`CiCatalogResource`](#cicatalogresource). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cicatalogresourceconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. | +| <a id="cicatalogresourceconnectionedges"></a>`edges` | [`[CiCatalogResourceEdge]`](#cicatalogresourceedge) | A list of edges. | +| <a id="cicatalogresourceconnectionnodes"></a>`nodes` | [`[CiCatalogResource]`](#cicatalogresource) | A list of nodes. | +| <a id="cicatalogresourceconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `CiCatalogResourceEdge` + +The edge type for [`CiCatalogResource`](#cicatalogresource). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cicatalogresourceedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="cicatalogresourceedgenode"></a>`node` | [`CiCatalogResource`](#cicatalogresource) | The item at the end of the edge. | + #### `CiConfigGroupConnection` The connection type for [`CiConfigGroup`](#ciconfiggroup). @@ -11678,6 +11722,17 @@ Represents the total number of issues and their weights for a particular day. | <a id="cibuildneedid"></a>`id` | [`ID!`](#id) | ID of the BuildNeed. | | <a id="cibuildneedname"></a>`name` | [`String`](#string) | Name of the job we need to complete. | +### `CiCatalogResource` + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cicatalogresourcedescription"></a>`description` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.11. This feature is in Alpha. It can be changed or removed at any time. Description of the catalog resource. | +| <a id="cicatalogresourceicon"></a>`icon` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.11. This feature is in Alpha. It can be changed or removed at any time. Icon for the catalog resource. | +| <a id="cicatalogresourceid"></a>`id` **{warning-solid}** | [`ID!`](#id) | **Introduced** in 15.11. This feature is in Alpha. It can be changed or removed at any time. ID of the catalog resource. | +| <a id="cicatalogresourcename"></a>`name` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.11. This feature is in Alpha. It can be changed or removed at any time. Name of the catalog resource. | + ### `CiConfig` #### Fields diff --git a/doc/api/visual_review_discussions.md b/doc/api/visual_review_discussions.md index 561dc1a26a5..496c732b337 100644 --- a/doc/api/visual_review_discussions.md +++ b/doc/api/visual_review_discussions.md @@ -4,13 +4,18 @@ group: Pipeline Execution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Visual Review discussions API **(PREMIUM)** +<!--- start_remove The following content will be removed on remove_date: '2024-05-22' --> +# Visual Review discussions API (deprecated) **(PREMIUM)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18710) in GitLab 12.5. > - [Moved](https://about.gitlab.com/blog/2021/01/26/new-gitlab-product-subscription-model/) to GitLab Premium in 13.9. +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387751) in GitLab 15.8 +and is planned for removal in 17.0. This change is a breaking change. + Visual Review discussions are notes on merge requests sent as -feedback from [Visual Reviews](../ci/review_apps/index.md#visual-reviews). +feedback from [Visual Reviews](../ci/review_apps/index.md#visual-reviews-deprecated). ## Create new merge request thread @@ -45,3 +50,4 @@ Parameters: ```shell curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/11/visual_review_discussions?body=comment" ``` +<!--- end_remove --> diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index fb0bf7b2ae1..1b423f1df70 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -193,13 +193,18 @@ After you have the route mapping set up, it takes effect in the following locati ![View on environment button in file view](img/view_on_env_blob.png) -## Visual Reviews **(PREMIUM)** +<!--- start_remove The following content will be removed on remove_date: '2024-05-22' --> +## Visual Reviews (deprecated) **(PREMIUM)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10761) in GitLab 12.0. > - [Moved](https://about.gitlab.com/blog/2021/01/26/new-gitlab-product-subscription-model/) to GitLab Premium in 13.9. > - It's [deployed behind a feature flag](../../user/feature_flags.md), `anonymous_visual_review_feedback`, disabled by default. > - It's disabled on GitLab.com. +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387751) in GitLab 15.8 +and is planned for removal in 17.0. This change is a breaking change. + FLAG: On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `anonymous_visual_review_feedback`. @@ -305,3 +310,5 @@ the user must enter a [personal access token](../../user/profile/personal_access with `api` scope before submitting feedback. This same method can be used to require authentication for any public projects. + +<!--- end_remove --> diff --git a/doc/ci/testing/accessibility_testing.md b/doc/ci/testing/accessibility_testing.md index 9d479289e07..b03e4a23153 100644 --- a/doc/ci/testing/accessibility_testing.md +++ b/doc/ci/testing/accessibility_testing.md @@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25144) in GitLab 12.8. WARNING: -This feature was deprecated in GitLab 15.9 +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/390424) in GitLab 15.9 and is planned for removal in 17.0. This change is a breaking change. If your application offers a web interface, you can use diff --git a/doc/ci/testing/browser_performance_testing.md b/doc/ci/testing/browser_performance_testing.md index 175ecef7f09..600b1a2cf4b 100644 --- a/doc/ci/testing/browser_performance_testing.md +++ b/doc/ci/testing/browser_performance_testing.md @@ -4,10 +4,15 @@ group: Pipeline Execution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- +<!--- start_remove The following content will be removed on remove_date: '2024-05-22' --> # Browser Performance Testing **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3507) in GitLab 10.3. +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/388719) in GitLab 15.9 +and is planned for removal in 17.0. This change is a breaking change. + If your application offers a web interface and you're using [GitLab CI/CD](../index.md), you can quickly determine the rendering performance impact of pending code changes in the browser. diff --git a/doc/ci/testing/load_performance_testing.md b/doc/ci/testing/load_performance_testing.md index dac4dd555b0..2897d7fe0ab 100644 --- a/doc/ci/testing/load_performance_testing.md +++ b/doc/ci/testing/load_performance_testing.md @@ -4,10 +4,15 @@ group: Pipeline Execution info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Load Performance Testing **(PREMIUM)** +<!--- start_remove The following content will be removed on remove_date: '2024-05-22' --> +# Load Performance Testing (deprecated) **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10683) in GitLab 13.2. +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/388723) in GitLab 15.9 +and is planned for removal in 17.0. This change is a breaking change. + With Load Performance Testing, you can test the impact of any pending code changes to your application's backend in [GitLab CI/CD](../index.md). @@ -199,3 +204,5 @@ load_performance: rules: - if: $CI_COMMIT_BRANCH # Modify to match your pipeline rules, or use `only/except` if needed. ``` + +<!--- end_remove --> diff --git a/doc/install/installation.md b/doc/install/installation.md index 1cd6a4fc5d2..b14ff89c804 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -224,7 +224,7 @@ Download Ruby and compile it: mkdir /tmp/ruby && cd /tmp/ruby curl --remote-name --location --progress-bar "https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.5.tar.gz" echo '9afc6380a027a4fe1ae1a3e2eccb6b497b9c5ac0631c12ca56f9b7beb4848776 ruby-3.0.5.tar.gz' | sha256sum -c - && tar xzf ruby-3.0.5.tar.gz -cd ruby-2.7.6 +cd ruby-3.0.5 ./configure --disable-install-rdoc --enable-shared make diff --git a/doc/subscriptions/bronze_starter.md b/doc/subscriptions/bronze_starter.md index accc0627a41..632177754f4 100644 --- a/doc/subscriptions/bronze_starter.md +++ b/doc/subscriptions/bronze_starter.md @@ -71,7 +71,7 @@ the tiers are no longer mentioned in GitLab documentation: - [Code Owners as eligible approvers](../user/project/merge_requests/approvals/rules.md#code-owners-as-eligible-approvers) - [Approval rules](../user/project/merge_requests/approvals/rules.md) features - [Restricting push and merge access to certain users](../user/project/protected_branches.md) - - [Visual Reviews](../ci/review_apps/index.md#visual-reviews) + - [Visual Reviews (deprecated)](../ci/review_apps/index.md#visual-reviews-deprecated) - Metrics and analytics: - [Contribution Analytics](../user/group/contribution_analytics/index.md) - [Merge Request Analytics](../user/analytics/merge_request_analytics.md) diff --git a/doc/subscriptions/quarterly_reconciliation.md b/doc/subscriptions/quarterly_reconciliation.md index 126a34334c4..16f1828f2c3 100644 --- a/doc/subscriptions/quarterly_reconciliation.md +++ b/doc/subscriptions/quarterly_reconciliation.md @@ -91,7 +91,7 @@ sent and subject to your payment terms. - You purchased your subscription from a reseller or another channel partner. - You purchased a multi-year subscription. - You purchased your subscription with a purchasing order. -- You are a pubic sector customer. +- You are a public sector customer. - You have an offline environment and used a license file to activate your subscription. - You are enrolled in a program that provides a free tier such as the GitLab for Education, GitLab for Open Source Program, or GitLab for Startups. diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb index 7a064fb4005..7249cb3e73b 100644 --- a/lib/gitlab/database/batch_count.rb +++ b/lib/gitlab/database/batch_count.rb @@ -27,7 +27,7 @@ # batch_sum(User, :sign_in_count) # batch_sum(Issue.group(:state_id), :weight)) # batch_average(Ci::Pipeline, :duration) -# batch_average(MergeTrain.group(:status), :duration) +# batch_average(MergeTrains::Car.group(:status), :duration) module Gitlab module Database module BatchCount diff --git a/lib/gitlab/database/schema_validation/inconsistency.rb b/lib/gitlab/database/schema_validation/inconsistency.rb index ea85429364b..c834a6bd693 100644 --- a/lib/gitlab/database/schema_validation/inconsistency.rb +++ b/lib/gitlab/database/schema_validation/inconsistency.rb @@ -18,6 +18,10 @@ module Gitlab validator_class.name.demodulize.underscore end + def table_name + structure_sql_object&.table_name || database_object&.table_name + end + def object_name structure_sql_object&.name || database_object&.name end diff --git a/lib/gitlab/database/schema_validation/index.rb b/lib/gitlab/database/schema_validation/index.rb deleted file mode 100644 index af0d5f31f4e..00000000000 --- a/lib/gitlab/database/schema_validation/index.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Database - module SchemaValidation - class Index - def initialize(parsed_stmt) - @parsed_stmt = parsed_stmt - end - - def name - parsed_stmt.idxname - end - - def statement - @statement ||= PgQuery.deparse_stmt(parsed_stmt) - end - - private - - attr_reader :parsed_stmt - end - end - end -end diff --git a/lib/gitlab/database/schema_validation/schema_inconsistency.rb b/lib/gitlab/database/schema_validation/schema_inconsistency.rb new file mode 100644 index 00000000000..6f50603e784 --- /dev/null +++ b/lib/gitlab/database/schema_validation/schema_inconsistency.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module SchemaValidation + class SchemaInconsistency < ApplicationRecord + self.table_name = :schema_inconsistencies + + belongs_to :issue + + validates :object_name, :valitador_name, :table_name, presence: true + end + end + end +end diff --git a/lib/gitlab/database/schema_validation/schema_objects/base.rb b/lib/gitlab/database/schema_validation/schema_objects/base.rb index b0c8eb087dd..43d30dc54ae 100644 --- a/lib/gitlab/database/schema_validation/schema_objects/base.rb +++ b/lib/gitlab/database/schema_validation/schema_objects/base.rb @@ -13,6 +13,10 @@ module Gitlab raise NoMethodError, "subclasses of #{self.class.name} must implement #{__method__}" end + def table_name + parsed_stmt.relation.relname + end + def statement @statement ||= PgQuery.deparse_stmt(parsed_stmt) end diff --git a/lib/gitlab/database/schema_validation/track_inconsistency.rb b/lib/gitlab/database/schema_validation/track_inconsistency.rb new file mode 100644 index 00000000000..c7e946be647 --- /dev/null +++ b/lib/gitlab/database/schema_validation/track_inconsistency.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module SchemaValidation + class TrackInconsistency + def initialize(inconsistency, project, user) + @inconsistency = inconsistency + @project = project + @user = user + end + + def execute + return unless Gitlab.com? + return if inconsistency_record.present? + + result = ::Issues::CreateService.new(container: project, current_user: user, params: params, + spam_params: nil).execute + + track_inconsistency(result[:issue]) if result.success? + end + + private + + attr_reader :inconsistency, :project, :user + + def track_inconsistency(issue) + schema_inconsistency_model.create( + issue: issue, + object_name: inconsistency.object_name, + table_name: inconsistency.table_name, + valitador_name: inconsistency.type + ) + end + + def params + { + title: issue_title, + description: issue_description, + confidential: true, + issue_type: 'issue', + labels: %w[database database-inconsistency-report] + } + end + + def issue_title + "New schema inconsistency: #{inconsistency.object_name}" + end + + def issue_description + <<~MSG + We have detected a new schema inconsistency. + + Table_name: #{inconsistency.table_name} + Object_name: #{inconsistency.object_name} + Validator_name: #{inconsistency.type} + Error_message: #{inconsistency.error_message} + + For more information, please contact the database team. + MSG + end + + def schema_inconsistency_model + Gitlab::Database::SchemaValidation::SchemaInconsistency + end + + def inconsistency_record + schema_inconsistency_model.find_by( + object_name: inconsistency.object_name, + table_name: inconsistency.table_name, + valitador_name: inconsistency.type + ) + end + end + end + end +end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index e7c89d6a728..a58eea0d880 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -455,7 +455,15 @@ namespace :gitlab do inconsistencies = Gitlab::Database::SchemaValidation::Runner.new(structure_sql, database).execute + gitlab_url = 'gitlab-org/gitlab' + inconsistencies.each do |inconsistency| + Gitlab::Database::SchemaValidation::TrackInconsistency.new( + inconsistency, + Project.find_by_full_path(gitlab_url), + User.support_bot + ).execute + puts inconsistency.inspect end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 56e0c0cd5f2..fd54b44b16b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6180,7 +6180,7 @@ msgstr "" msgid "Authorized applications (%{size})" msgstr "" -msgid "AuthorizedApplication|Application secret was successfully updated." +msgid "AuthorizedApplication|Application secret was successfully renewed." msgstr "" msgid "AuthorizedApplication|Are you sure you want to renew this secret? Any applications using the old secret will no longer be able to authenticate with GitLab." @@ -6192,9 +6192,15 @@ msgstr "" msgid "AuthorizedApplication|Renew secret" msgstr "" +msgid "AuthorizedApplication|Renew secret?" +msgstr "" + msgid "AuthorizedApplication|Revoke application" msgstr "" +msgid "AuthorizedApplication|There was an error trying to renew the application secret. Please try again." +msgstr "" + msgid "Authors: %{authors}" msgstr "" diff --git a/spec/controllers/admin/applications_controller_spec.rb b/spec/controllers/admin/applications_controller_spec.rb index edb17aefe86..1feda0ed36f 100644 --- a/spec/controllers/admin/applications_controller_spec.rb +++ b/spec/controllers/admin/applications_controller_spec.rb @@ -50,6 +50,12 @@ RSpec.describe Admin::ApplicationsController do it { is_expected.to have_gitlab_http_status(:ok) } it { expect { subject }.to change { application.reload.secret } } + it 'returns the secret in json format' do + subject + + expect(json_response['secret']).not_to be_nil + end + context 'when renew fails' do before do allow_next_found_instance_of(Doorkeeper::Application) do |application| @@ -58,7 +64,7 @@ RSpec.describe Admin::ApplicationsController do end it { expect { subject }.not_to change { application.reload.secret } } - it { is_expected.to redirect_to(admin_application_url(application)) } + it { is_expected.to have_gitlab_http_status(:unprocessable_entity) } end end diff --git a/spec/controllers/groups/settings/applications_controller_spec.rb b/spec/controllers/groups/settings/applications_controller_spec.rb index 2fadac2dc17..c398fd044c2 100644 --- a/spec/controllers/groups/settings/applications_controller_spec.rb +++ b/spec/controllers/groups/settings/applications_controller_spec.rb @@ -156,6 +156,12 @@ RSpec.describe Groups::Settings::ApplicationsController do it { is_expected.to have_gitlab_http_status(:ok) } it { expect { subject }.to change { application.reload.secret } } + it 'returns the secret in json format' do + subject + + expect(json_response['secret']).not_to be_nil + end + context 'when renew fails' do before do allow_next_found_instance_of(Doorkeeper::Application) do |application| @@ -164,7 +170,7 @@ RSpec.describe Groups::Settings::ApplicationsController do end it { expect { subject }.not_to change { application.reload.secret } } - it { is_expected.to redirect_to(group_settings_application_url(group, application)) } + it { is_expected.to have_gitlab_http_status(:unprocessable_entity) } end end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index e7ec268a5a2..5b9fd192ad4 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -86,6 +86,12 @@ RSpec.describe Oauth::ApplicationsController do it_behaves_like 'redirects to login page when the user is not signed in' it_behaves_like 'redirects to 2fa setup page when the user requires it' + it 'returns the secret in json format' do + subject + + expect(json_response['secret']).not_to be_nil + end + context 'when renew fails' do before do allow_next_found_instance_of(Doorkeeper::Application) do |application| @@ -94,7 +100,7 @@ RSpec.describe Oauth::ApplicationsController do end it { expect { subject }.not_to change { application.reload.secret } } - it { is_expected.to redirect_to(oauth_application_url(application)) } + it { is_expected.to have_gitlab_http_status(:unprocessable_entity) } end end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index e3e08d3b653..e4a0ccdb57f 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -11,7 +11,8 @@ RSpec.describe 'Database schema', feature_category: :database do IGNORED_INDEXES_ON_FKS = { slack_integrations_scopes: %w[slack_api_scope_id], - p_ci_builds_metadata: %w[partition_id] # composable FK, the columns are reversed in the index definition + p_ci_builds_metadata: %w[partition_id], # composable FK, the columns are reversed in the index definition + p_ci_runner_machine_builds: %w[partition_id] # composable FK, the columns are reversed in the index definition }.with_indifferent_access.freeze TABLE_PARTITIONS = %w[ci_builds_metadata].freeze @@ -319,7 +320,7 @@ RSpec.describe 'Database schema', feature_category: :database do # position. Using PARTITIONABLE_MODELS instead of iterating tables since when partitioning existing tables, # the routing table only gets created after the PK has already been created, which would be too late for a check. - skip_tables = %w[Ci::RunnerMachineBuild] + skip_tables = %w[] partitionable_models = Ci::Partitionable::Testing::PARTITIONABLE_MODELS (partitionable_models - skip_tables).each do |klass| model = klass.safe_constantize diff --git a/spec/factories/gitlab/database/background_migration/schema_inconsistencies.rb b/spec/factories/gitlab/database/background_migration/schema_inconsistencies.rb new file mode 100644 index 00000000000..b71b0971417 --- /dev/null +++ b/spec/factories/gitlab/database/background_migration/schema_inconsistencies.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :schema_inconsistency, class: '::Gitlab::Database::SchemaValidation::SchemaInconsistency' do + issue factory: :issue + + object_name { 'name' } + table_name { 'table' } + valitador_name { 'validator' } + end +end diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js index 6247cfcc640..19033c227d9 100644 --- a/spec/frontend/jobs/components/table/job_table_app_spec.js +++ b/spec/frontend/jobs/components/table/job_table_app_spec.js @@ -275,5 +275,42 @@ describe('Job table app', () => { url: `${TEST_HOST}/?statuses=FAILED`, }); }); + + it('resets query param after clearing tokens', () => { + createComponent(); + + jest.spyOn(urlUtils, 'updateHistory'); + + findFilteredSearch().vm.$emit('filterJobsBySearch', [mockFailedSearchToken]); + + expect(successHandler).toHaveBeenCalledWith({ + first: 30, + fullPath: 'gitlab-org/gitlab', + statuses: 'FAILED', + }); + expect(countSuccessHandler).toHaveBeenCalledWith({ + fullPath: 'gitlab-org/gitlab', + statuses: 'FAILED', + }); + expect(urlUtils.updateHistory).toHaveBeenCalledWith({ + url: `${TEST_HOST}/?statuses=FAILED`, + }); + + findFilteredSearch().vm.$emit('filterJobsBySearch', []); + + expect(urlUtils.updateHistory).toHaveBeenCalledWith({ + url: `${TEST_HOST}/`, + }); + + expect(successHandler).toHaveBeenCalledWith({ + first: 30, + fullPath: 'gitlab-org/gitlab', + statuses: null, + }); + expect(countSuccessHandler).toHaveBeenCalledWith({ + fullPath: 'gitlab-org/gitlab', + statuses: null, + }); + }); }); }); diff --git a/spec/frontend/oauth_application/components/oauth_secret_spec.js b/spec/frontend/oauth_application/components/oauth_secret_spec.js new file mode 100644 index 00000000000..c38bd066da8 --- /dev/null +++ b/spec/frontend/oauth_application/components/oauth_secret_spec.js @@ -0,0 +1,116 @@ +import { GlButton, GlModal } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; +import waitForPromises from 'helpers/wait_for_promises'; +import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/alert'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; +import OAuthSecret from '~/oauth_application/components/oauth_secret.vue'; +import { + RENEW_SECRET_FAILURE, + RENEW_SECRET_SUCCESS, + WARNING_NO_SECRET, +} from '~/oauth_application/constants'; +import InputCopyToggleVisibility from '~/vue_shared/components/form/input_copy_toggle_visibility.vue'; + +jest.mock('~/alert'); +const mockEvent = { preventDefault: jest.fn() }; + +describe('OAuthSecret', () => { + let wrapper; + const renewPath = '/applications/1/renew'; + + const createComponent = (provide = {}) => { + wrapper = shallowMount(OAuthSecret, { + provide: { + initialSecret: undefined, + renewPath, + ...provide, + }, + }); + }; + + const findInputCopyToggleVisibility = () => wrapper.findComponent(InputCopyToggleVisibility); + const findRenewSecretButton = () => wrapper.findComponent(GlButton); + const findModal = () => wrapper.findComponent(GlModal); + + describe('when secret is provided', () => { + const initialSecret = 'my secret'; + beforeEach(() => { + createComponent({ initialSecret }); + }); + + it('shows the masked secret', () => { + expect(findInputCopyToggleVisibility().props('value')).toBe(initialSecret); + }); + + it('shows the renew secret button', () => { + expect(findRenewSecretButton().exists()).toBe(true); + }); + }); + + describe('when secret is not provided', () => { + beforeEach(() => { + createComponent(); + }); + + it('shows an alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: WARNING_NO_SECRET, + variant: VARIANT_WARNING, + }); + }); + + it('shows the renew secret button', () => { + expect(findRenewSecretButton().exists()).toBe(true); + }); + + describe('when renew secret button is selected', () => { + beforeEach(() => { + createComponent(); + findRenewSecretButton().vm.$emit('click'); + }); + + it('shows a modal', () => { + expect(findModal().props('visible')).toBe(true); + }); + + describe('when secret renewal succeeds', () => { + const initialSecret = 'my secret'; + + beforeEach(async () => { + const mockAxios = new MockAdapter(axios); + mockAxios.onPut().reply(HTTP_STATUS_OK, { secret: initialSecret }); + findModal().vm.$emit('primary', mockEvent); + await waitForPromises(); + }); + + it('shows an alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: RENEW_SECRET_SUCCESS, + variant: VARIANT_SUCCESS, + }); + }); + + it('shows the new secret', () => { + expect(findInputCopyToggleVisibility().props('value')).toBe(initialSecret); + }); + }); + + describe('when secret renewal fails', () => { + beforeEach(async () => { + const mockAxios = new MockAdapter(axios); + mockAxios.onPut().reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); + findModal().vm.$emit('primary', mockEvent); + await waitForPromises(); + }); + + it('creates an alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: RENEW_SECRET_FAILURE, + }); + }); + }); + }); + }); +}); diff --git a/spec/graphql/types/ci/catalog/resource_type_spec.rb b/spec/graphql/types/ci/catalog/resource_type_spec.rb new file mode 100644 index 00000000000..d0bb45a4f1d --- /dev/null +++ b/spec/graphql/types/ci/catalog/resource_type_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Ci::Catalog::ResourceType, feature_category: :pipeline_composition do + specify { expect(described_class.graphql_name).to eq('CiCatalogResource') } + + it 'exposes the expected fields' do + expected_fields = %i[ + id + name + description + icon + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index cef72d24c43..6eb97a99264 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -297,22 +297,26 @@ RSpec.describe AvatarsHelper do subject { helper.user_avatar_without_link(options) } it 'displays user avatar' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 16), - data: { container: 'body' }, - class: 'avatar s16 has-tooltip', - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 16), + data: { container: 'body' }, + class: 'avatar s16 has-tooltip', + title: user.name + ) end context 'with css_class parameter' do let(:options) { { user: user, css_class: '.cat-pics' } } it 'uses provided css_class' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 16), - data: { container: 'body' }, - class: "avatar s16 #{options[:css_class]} has-tooltip", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 16), + data: { container: 'body' }, + class: "avatar s16 #{options[:css_class]} has-tooltip", + title: user.name + ) end end @@ -320,11 +324,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, size: 99 } } it 'uses provided size' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, options[:size]), - data: { container: 'body' }, - class: "avatar s#{options[:size]} has-tooltip", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, options[:size]), + data: { container: 'body' }, + class: "avatar s#{options[:size]} has-tooltip", + title: user.name + ) end end @@ -332,11 +338,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, url: '/over/the/rainbow.png' } } it 'uses provided url' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: options[:url], - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: options[:url], + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: user.name + ) end end @@ -344,11 +352,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, lazy: true } } it 'adds `lazy` class to class list, sets `data-src` with avatar URL and `src` with placeholder image' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: LazyImageTagHelper.placeholder_image, - data: { container: 'body', src: avatar_icon_for_user(user, 16) }, - class: "avatar s16 has-tooltip lazy", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: LazyImageTagHelper.placeholder_image, + data: { container: 'body', src: avatar_icon_for_user(user, 16) }, + class: "avatar s16 has-tooltip lazy", + title: user.name + ) end end @@ -357,11 +367,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, has_tooltip: true } } it 'adds has-tooltip' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 16), - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 16), + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: user.name + ) end end @@ -369,10 +381,12 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, has_tooltip: false } } it 'does not add has-tooltip or data container' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 16), - class: "avatar s16", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 16), + class: "avatar s16", + title: user.name + ) end end end @@ -384,20 +398,24 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user, user_name: 'Tinky Winky' } } it 'prefers user parameter' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 16), - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 16), + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: user.name + ) end end it 'uses user_name and user_email parameter if user is not present' do - is_expected.to eq tag.img(alt: "#{options[:user_name]}'s avatar", - src: helper.avatar_icon_for_email(options[:user_email], 16), - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: options[:user_name]) + is_expected.to eq tag.img( + alt: "#{options[:user_name]}'s avatar", + src: helper.avatar_icon_for_email(options[:user_email], 16), + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: options[:user_name] + ) end end @@ -408,11 +426,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user: user_with_avatar, only_path: false } } it 'will return avatar with a full path' do - is_expected.to eq tag.img(alt: "#{user_with_avatar.name}'s avatar", - src: avatar_icon_for_user(user_with_avatar, 16, only_path: false), - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: user_with_avatar.name) + is_expected.to eq tag.img( + alt: "#{user_with_avatar.name}'s avatar", + src: avatar_icon_for_user(user_with_avatar, 16, only_path: false), + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: user_with_avatar.name + ) end end @@ -420,11 +440,13 @@ RSpec.describe AvatarsHelper do let(:options) { { user_email: user_with_avatar.email, user_name: user_with_avatar.username, only_path: false } } it 'will return avatar with a full path' do - is_expected.to eq tag.img(alt: "#{user_with_avatar.username}'s avatar", - src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false), - data: { container: 'body' }, - class: "avatar s16 has-tooltip", - title: user_with_avatar.username) + is_expected.to eq tag.img( + alt: "#{user_with_avatar.username}'s avatar", + src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false), + data: { container: 'body' }, + class: "avatar s16 has-tooltip", + title: user_with_avatar.username + ) end end end @@ -447,11 +469,13 @@ RSpec.describe AvatarsHelper do let(:resource) { user.namespace } it 'displays user avatar' do - is_expected.to eq tag.img(alt: "#{user.name}'s avatar", - src: avatar_icon_for_user(user, 32), - data: { container: 'body' }, - class: 'avatar s32 has-tooltip', - title: user.name) + is_expected.to eq tag.img( + alt: "#{user.name}'s avatar", + src: avatar_icon_for_user(user, 32), + data: { container: 'body' }, + class: 'avatar s32 has-tooltip', + title: user.name + ) end end diff --git a/spec/helpers/emoji_helper_spec.rb b/spec/helpers/emoji_helper_spec.rb index 6f4c962c0fb..e16c96c86ed 100644 --- a/spec/helpers/emoji_helper_spec.rb +++ b/spec/helpers/emoji_helper_spec.rb @@ -12,10 +12,12 @@ RSpec.describe EmojiHelper do subject { helper.emoji_icon(emoji_text, options) } it 'has no options' do - is_expected.to include('<gl-emoji', - "title=\"#{emoji_text}\"", - "data-name=\"#{emoji_text}\"", - "data-unicode-version=\"#{unicode_version}\"") + is_expected.to include( + '<gl-emoji', + "title=\"#{emoji_text}\"", + "data-name=\"#{emoji_text}\"", + "data-unicode-version=\"#{unicode_version}\"" + ) is_expected.not_to include(aria_hidden_option) end @@ -23,11 +25,13 @@ RSpec.describe EmojiHelper do let(:options) { { 'aria-hidden': true } } it 'applies aria-hidden' do - is_expected.to include('<gl-emoji', - "title=\"#{emoji_text}\"", - "data-name=\"#{emoji_text}\"", - "data-unicode-version=\"#{unicode_version}\"", - aria_hidden_option) + is_expected.to include( + '<gl-emoji', + "title=\"#{emoji_text}\"", + "data-name=\"#{emoji_text}\"", + "data-unicode-version=\"#{unicode_version}\"", + aria_hidden_option + ) end end end diff --git a/spec/helpers/feature_flags_helper_spec.rb b/spec/helpers/feature_flags_helper_spec.rb index 786454c6c4d..a5e7f8d273e 100644 --- a/spec/helpers/feature_flags_helper_spec.rb +++ b/spec/helpers/feature_flags_helper_spec.rb @@ -33,12 +33,14 @@ RSpec.describe FeatureFlagsHelper do subject { helper.edit_feature_flag_data } it 'contains all the data needed to edit feature flags' do - is_expected.to include(endpoint: "/#{project.full_path}/-/feature_flags/#{feature_flag.iid}", - project_id: project.id, - feature_flags_path: "/#{project.full_path}/-/feature_flags", - environments_endpoint: "/#{project.full_path}/-/environments/search.json", - strategy_type_docs_page_path: "/help/operations/feature_flags#feature-flag-strategies", - environments_scope_docs_path: "/help/ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable") + is_expected.to include( + endpoint: "/#{project.full_path}/-/feature_flags/#{feature_flag.iid}", + project_id: project.id, + feature_flags_path: "/#{project.full_path}/-/feature_flags", + environments_endpoint: "/#{project.full_path}/-/environments/search.json", + strategy_type_docs_page_path: "/help/operations/feature_flags#feature-flag-strategies", + environments_scope_docs_path: "/help/ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable" + ) end end end diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb index 3e6780d6831..e288a604be6 100644 --- a/spec/helpers/namespaces_helper_spec.rb +++ b/spec/helpers/namespaces_helper_spec.rb @@ -6,38 +6,35 @@ RSpec.describe NamespacesHelper do let!(:admin) { create(:admin) } let!(:admin_project_creation_level) { nil } let!(:admin_group) do - create(:group, - :private, - project_creation_level: admin_project_creation_level) + create(:group, :private, project_creation_level: admin_project_creation_level) end let!(:user) { create(:user) } let!(:user_project_creation_level) { nil } let!(:user_group) do - create(:group, - :private, - project_creation_level: user_project_creation_level) + create(:group, :private, project_creation_level: user_project_creation_level) end let!(:subgroup1) do - create(:group, - :private, - parent: admin_group, - project_creation_level: nil) + create(:group, :private, parent: admin_group, project_creation_level: nil) end let!(:subgroup2) do - create(:group, - :private, - parent: admin_group, - project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) + create( + :group, + :private, + parent: admin_group, + project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS + ) end let!(:subgroup3) do - create(:group, - :private, - parent: admin_group, - project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) + create( + :group, + :private, + parent: admin_group, + project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + ) end before do diff --git a/spec/helpers/notify_helper_spec.rb b/spec/helpers/notify_helper_spec.rb index 09da2b89dff..bc1b927cc93 100644 --- a/spec/helpers/notify_helper_spec.rb +++ b/spec/helpers/notify_helper_spec.rb @@ -64,10 +64,19 @@ RSpec.describe NotifyHelper do mr_link_style = "font-weight: 600;color:#3777b0;text-decoration:none" reviewer_avatar_style = "border-radius:12px;margin:-7px 0 -7px 3px;" mr_link = link_to(merge_request.to_reference, merge_request_url(merge_request), style: mr_link_style).html_safe - reviewer_avatar = content_tag(:img, nil, height: "24", src: avatar_icon_for_user, style: reviewer_avatar_style, \ - width: "24", alt: "Avatar", class: "avatar").html_safe - reviewer_link = link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", \ - class: "muted").html_safe + reviewer_avatar = content_tag( + :img, + nil, + height: "24", + src: avatar_icon_for_user, + style: reviewer_avatar_style, + width: "24", + alt: "Avatar", + class: "avatar" + ).html_safe + reviewer_link = link_to( + reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", class: "muted" + ).html_safe result = helper.merge_request_hash_param(merge_request, reviewer) expect(result[:mr_highlight]).to eq '<span style="font-weight: 600;color:#333333;">'.html_safe expect(result[:highlight_end]).to eq '</span>'.html_safe diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index eb42ce18da0..9694921e223 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -128,12 +128,14 @@ RSpec.describe PageLayoutHelper do describe 'a bare controller' do it 'returns an empty context' do - expect(search_context).to have_attributes(project: nil, - group: nil, - snippets: [], - project_metadata: {}, - group_metadata: {}, - search_url: '/search') + expect(search_context).to have_attributes( + project: nil, + group: nil, + snippets: [], + project_metadata: {}, + group_metadata: {}, + search_url: '/search' + ) end end end diff --git a/spec/helpers/routing/pseudonymization_helper_spec.rb b/spec/helpers/routing/pseudonymization_helper_spec.rb index eb2cb548f35..784579dc895 100644 --- a/spec/helpers/routing/pseudonymization_helper_spec.rb +++ b/spec/helpers/routing/pseudonymization_helper_spec.rb @@ -26,17 +26,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'with controller for MR' do let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/merge_requests/#{merge_request.id}" } let(:request) do - double(:Request, - path_parameters: { - controller: "projects/merge_requests", - action: "show", - namespace_id: group.name, - project_id: project.name, - id: merge_request.id.to_s - }, - protocol: 'http', - host: 'localhost', - query_string: '') + double( + :Request, + path_parameters: { + controller: "projects/merge_requests", + action: "show", + namespace_id: group.name, + project_id: project.name, + id: merge_request.id.to_s + }, + protocol: 'http', + host: 'localhost', + query_string: '' + ) end before do @@ -49,17 +51,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'with controller for issue' do let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/issues/#{issue.id}" } let(:request) do - double(:Request, - path_parameters: { - controller: "projects/issues", - action: "show", - namespace_id: group.name, - project_id: project.name, - id: issue.id.to_s - }, - protocol: 'http', - host: 'localhost', - query_string: '') + double( + :Request, + path_parameters: { + controller: "projects/issues", + action: "show", + namespace_id: group.name, + project_id: project.name, + id: issue.id.to_s + }, + protocol: 'http', + host: 'localhost', + query_string: '' + ) end before do @@ -74,16 +78,18 @@ RSpec.describe ::Routing::PseudonymizationHelper do let(:group) { subgroup } let(:project) { subproject } let(:request) do - double(:Request, - path_parameters: { - controller: 'projects', - action: 'show', - namespace_id: subgroup.name, - id: subproject.name - }, - protocol: 'http', - host: 'localhost', - query_string: '') + double( + :Request, + path_parameters: { + controller: 'projects', + action: 'show', + namespace_id: subgroup.name, + id: subproject.name + }, + protocol: 'http', + host: 'localhost', + query_string: '' + ) end before do @@ -97,15 +103,17 @@ RSpec.describe ::Routing::PseudonymizationHelper do let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared" } let(:group) { subgroup } let(:request) do - double(:Request, - path_parameters: { - controller: 'groups', - action: 'show', - id: subgroup.name - }, - protocol: 'http', - host: 'localhost', - query_string: '') + double( + :Request, + path_parameters: { + controller: 'groups', + action: 'show', + id: subgroup.name + }, + protocol: 'http', + host: 'localhost', + query_string: '' + ) end before do @@ -118,17 +126,19 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'with controller for blob with file path' do let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/blob/:repository_path" } let(:request) do - double(:Request, - path_parameters: { - controller: 'projects/blob', - action: 'show', - namespace_id: group.name, - project_id: project.name, - id: 'master/README.md' - }, - protocol: 'http', - host: 'localhost', - query_string: '') + double( + :Request, + path_parameters: { + controller: 'projects/blob', + action: 'show', + namespace_id: group.name, + project_id: project.name, + id: 'master/README.md' + }, + protocol: 'http', + host: 'localhost', + query_string: '' + ) end before do @@ -141,14 +151,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'when assignee_username is present' do let(:masked_url) { "http://localhost/dashboard/issues?assignee_username=masked_assignee_username" } let(:request) do - double(:Request, - path_parameters: { - controller: 'dashboard', - action: 'issues' - }, - protocol: 'http', - host: 'localhost', - query_string: 'assignee_username=root') + double( + :Request, + path_parameters: { + controller: 'dashboard', + action: 'issues' + }, + protocol: 'http', + host: 'localhost', + query_string: 'assignee_username=root' + ) end before do @@ -161,14 +173,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'when author_username is present' do let(:masked_url) { "http://localhost/dashboard/issues?author_username=masked_author_username&scope=all&state=opened" } let(:request) do - double(:Request, - path_parameters: { - controller: 'dashboard', - action: 'issues' - }, - protocol: 'http', - host: 'localhost', - query_string: 'author_username=root&scope=all&state=opened') + double( + :Request, + path_parameters: { + controller: 'dashboard', + action: 'issues' + }, + protocol: 'http', + host: 'localhost', + query_string: 'author_username=root&scope=all&state=opened' + ) end before do @@ -181,14 +195,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'when some query params are not required to be masked' do let(:masked_url) { "http://localhost/dashboard/issues?author_username=masked_author_username&scope=all&state=masked_state&tab=2" } let(:request) do - double(:Request, - path_parameters: { - controller: 'dashboard', - action: 'issues' - }, - protocol: 'http', - host: 'localhost', - query_string: 'author_username=root&scope=all&state=opened&tab=2') + double( + :Request, + path_parameters: { + controller: 'dashboard', + action: 'issues' + }, + protocol: 'http', + host: 'localhost', + query_string: 'author_username=root&scope=all&state=opened&tab=2' + ) end before do @@ -202,14 +218,16 @@ RSpec.describe ::Routing::PseudonymizationHelper do context 'when query string has keys with the same names as path params' do let(:masked_url) { "http://localhost/dashboard/issues?action=masked_action&scope=all&state=opened" } let(:request) do - double(:Request, - path_parameters: { - controller: 'dashboard', - action: 'issues' - }, - protocol: 'http', - host: 'localhost', - query_string: 'action=foobar&scope=all&state=opened') + double( + :Request, + path_parameters: { + controller: 'dashboard', + action: 'issues' + }, + protocol: 'http', + host: 'localhost', + query_string: 'action=foobar&scope=all&state=opened' + ) end before do @@ -223,16 +241,18 @@ RSpec.describe ::Routing::PseudonymizationHelper do describe 'when url has no params to mask' do let(:original_url) { 'http://localhost/-/security/vulnerabilities' } let(:request) do - double(:Request, - path_parameters: { - controller: 'security/vulnerabilities', - action: 'index' - }, - protocol: 'http', - host: 'localhost', - query_string: '', - original_fullpath: '/-/security/vulnerabilities', - original_url: original_url) + double( + :Request, + path_parameters: { + controller: 'security/vulnerabilities', + action: 'index' + }, + protocol: 'http', + host: 'localhost', + query_string: '', + original_fullpath: '/-/security/vulnerabilities', + original_url: original_url + ) end before do @@ -247,15 +267,17 @@ RSpec.describe ::Routing::PseudonymizationHelper do describe 'when it raises exception' do context 'calls error tracking' do let(:request) do - double(:Request, - path_parameters: { - controller: 'dashboard', - action: 'issues' - }, - protocol: 'http', - host: 'localhost', - query_string: 'assignee_username=root', - original_fullpath: '/dashboard/issues?assignee_username=root') + double( + :Request, + path_parameters: { + controller: 'dashboard', + action: 'issues' + }, + protocol: 'http', + host: 'localhost', + query_string: 'assignee_username=root', + original_fullpath: '/dashboard/issues?assignee_username=root' + ) end before do diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb index 6c0f1034d65..d62da2ca714 100644 --- a/spec/helpers/storage_helper_spec.rb +++ b/spec/helpers/storage_helper_spec.rb @@ -24,18 +24,22 @@ RSpec.describe StorageHelper do describe "#storage_counters_details" do let_it_be(:namespace) { create(:namespace) } let_it_be(:project) do - create(:project, - namespace: namespace, - statistics: build(:project_statistics, - namespace: namespace, - repository_size: 10.kilobytes, - wiki_size: 10.bytes, - lfs_objects_size: 20.gigabytes, - build_artifacts_size: 30.megabytes, - pipeline_artifacts_size: 11.megabytes, - snippets_size: 40.megabytes, - packages_size: 12.megabytes, - uploads_size: 15.megabytes)) + create( + :project, + namespace: namespace, + statistics: build( + :project_statistics, + namespace: namespace, + repository_size: 10.kilobytes, + wiki_size: 10.bytes, + lfs_objects_size: 20.gigabytes, + build_artifacts_size: 30.megabytes, + pipeline_artifacts_size: 11.megabytes, + snippets_size: 40.megabytes, + packages_size: 12.megabytes, + uploads_size: 15.megabytes + ) + ) end let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / Pipeline Artifacts: 11 MB / LFS: 20 GB / Snippets: 40 MB / Packages: 12 MB / Uploads: 15 MB' } diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb index 8d24e9576e0..54b7c5ce11e 100644 --- a/spec/helpers/todos_helper_spec.rb +++ b/spec/helpers/todos_helper_spec.rb @@ -9,20 +9,21 @@ RSpec.describe TodosHelper do let_it_be(:issue) { create(:issue, title: 'Issue 1', project: project) } let_it_be(:design) { create(:design, issue: issue) } let_it_be(:note) do - create(:note, - project: issue.project, - note: 'I am note, hear me roar') + create(:note, project: issue.project, note: 'I am note, hear me roar') end let_it_be(:group) { create(:group, :public, name: 'Group 1') } let_it_be(:design_todo) do - create(:todo, :mentioned, - user: user, - project: project, - target: design, - author: author, - note: note) + create( + :todo, + :mentioned, + user: user, + project: project, + target: design, + author: author, + note: note + ) end let_it_be(:alert_todo) do @@ -93,11 +94,14 @@ RSpec.describe TodosHelper do context 'when given a non-design todo' do let(:todo) do - build_stubbed(:todo, :assigned, - user: user, - project: issue.project, - target: issue, - author: author) + build_stubbed( + :todo, + :assigned, + user: user, + project: issue.project, + target: issue, + author: author + ) end it 'returns the title' do @@ -154,11 +158,13 @@ RSpec.describe TodosHelper do context 'when a user requests access to group' do let_it_be(:group_access_request_todo) do - create(:todo, - target_id: group.id, - target_type: group.class.polymorphic_name, - group: group, - action: Todo::MEMBER_ACCESS_REQUESTED) + create( + :todo, + target_id: group.id, + target_type: group.class.polymorphic_name, + group: group, + action: Todo::MEMBER_ACCESS_REQUESTED + ) end it 'responds with access requests tab' do diff --git a/spec/helpers/users/group_callouts_helper_spec.rb b/spec/helpers/users/group_callouts_helper_spec.rb index da67c4921b3..c6679069c49 100644 --- a/spec/helpers/users/group_callouts_helper_spec.rb +++ b/spec/helpers/users/group_callouts_helper_spec.rb @@ -70,10 +70,12 @@ RSpec.describe Users::GroupCalloutsHelper do context 'when the invite_members_banner has been dismissed' do before do - create(:group_callout, - user: user, - group: group, - feature_name: described_class::INVITE_MEMBERS_BANNER) + create( + :group_callout, + user: user, + group: group, + feature_name: described_class::INVITE_MEMBERS_BANNER + ) end it { is_expected.to eq(false) } diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index a0bd629e3e6..8f37bf29a4b 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -178,8 +178,10 @@ RSpec.describe VisibilityLevelHelper, feature_category: :system_access do end before do - stub_application_setting(restricted_visibility_levels: restricted_levels, - default_project_visibility: global_default_level) + stub_application_setting( + restricted_visibility_levels: restricted_levels, + default_project_visibility: global_default_level + ) end with_them do diff --git a/spec/lib/gitlab/database/schema_validation/inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/inconsistency_spec.rb index 890aa38cc7f..cb3df75b3fb 100644 --- a/spec/lib/gitlab/database/schema_validation/inconsistency_spec.rb +++ b/spec/lib/gitlab/database/schema_validation/inconsistency_spec.rb @@ -11,8 +11,8 @@ RSpec.describe Gitlab::Database::SchemaValidation::Inconsistency, feature_catego let(:structure_stmt) { PgQuery.parse(structure_sql_statement).tree.stmts.first.stmt.index_stmt } let(:database_stmt) { PgQuery.parse(database_statement).tree.stmts.first.stmt.index_stmt } - let(:structure_sql_object) { Gitlab::Database::SchemaValidation::Index.new(structure_stmt) } - let(:database_object) { Gitlab::Database::SchemaValidation::Index.new(database_stmt) } + let(:structure_sql_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Index.new(structure_stmt) } + let(:database_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Index.new(database_stmt) } subject(:inconsistency) { described_class.new(validator, structure_sql_object, database_object) } @@ -44,6 +44,12 @@ RSpec.describe Gitlab::Database::SchemaValidation::Inconsistency, feature_catego end end + describe '#table_name' do + it 'returns the table name' do + expect(inconsistency.table_name).to eq('achievements') + end + end + describe '#inspect' do let(:expected_output) do <<~MSG diff --git a/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb new file mode 100644 index 00000000000..7d6a279def9 --- /dev/null +++ b/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::SchemaValidation::SchemaInconsistency, type: :model, feature_category: :database do + it { is_expected.to be_a ApplicationRecord } + + describe 'associations' do + it { is_expected.to belong_to(:issue) } + end + + describe "Validations" do + it { is_expected.to validate_presence_of(:object_name) } + it { is_expected.to validate_presence_of(:valitador_name) } + it { is_expected.to validate_presence_of(:table_name) } + end +end diff --git a/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb index 1aaa994e3bb..43d8fa38ec8 100644 --- a/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb +++ b/spec/lib/gitlab/database/schema_validation/schema_objects/index_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Gitlab::Database::SchemaValidation::SchemaObjects::Index, feature_category: :database do let(:statement) { 'CREATE INDEX index_name ON public.achievements USING btree (namespace_id)' } let(:name) { 'index_name' } + let(:table_name) { 'achievements' } include_examples 'schema objects assertions for', 'index_stmt' end diff --git a/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb index 8000a54ee27..3c2481dfae0 100644 --- a/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb +++ b/spec/lib/gitlab/database/schema_validation/schema_objects/trigger_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Gitlab::Database::SchemaValidation::SchemaObjects::Trigger, feature_category: :database do let(:statement) { 'CREATE TRIGGER my_trigger BEFORE INSERT ON todos FOR EACH ROW EXECUTE FUNCTION trigger()' } let(:name) { 'my_trigger' } + let(:table_name) { 'todos' } include_examples 'schema objects assertions for', 'create_trig_stmt' end diff --git a/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb new file mode 100644 index 00000000000..84db721fc2d --- /dev/null +++ b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::SchemaValidation::TrackInconsistency, feature_category: :database do + describe '#execute' do + let(:validator) { Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes } + + let(:database_statement) { 'CREATE INDEX index_name ON public.achievements USING btree (namespace_id)' } + let(:structure_sql_statement) { 'CREATE INDEX index_name ON public.achievements USING btree (id)' } + + let(:structure_stmt) { PgQuery.parse(structure_sql_statement).tree.stmts.first.stmt.index_stmt } + let(:database_stmt) { PgQuery.parse(database_statement).tree.stmts.first.stmt.index_stmt } + + let(:structure_sql_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Index.new(structure_stmt) } + let(:database_object) { Gitlab::Database::SchemaValidation::SchemaObjects::Index.new(database_stmt) } + + let(:inconsistency) do + Gitlab::Database::SchemaValidation::Inconsistency.new(validator, structure_sql_object, database_object) + end + + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + subject(:execute) { described_class.new(inconsistency, project, user).execute } + + before do + stub_spam_services + end + + context 'when is not GitLab.com' do + it 'does not create a schema inconsistency record' do + allow(Gitlab).to receive(:com?).and_return(false) + + expect { execute }.not_to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count } + end + end + + context 'when the issue creation fails' do + let(:issue_creation) { instance_double(Mutations::Issues::Create, resolve: { errors: 'error' }) } + + before do + allow(Mutations::Issues::Create).to receive(:new).and_return(issue_creation) + end + + it 'does not create a schema inconsistency record' do + allow(Gitlab).to receive(:com?).and_return(true) + + expect { execute }.not_to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count } + end + end + + context 'when a new inconsistency is found' do + before do + project.add_developer(user) + end + + it 'creates a new schema inconsistency record' do + allow(Gitlab).to receive(:com?).and_return(true) + + expect { execute }.to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count } + end + end + + context 'when the schema inconsistency already exists' do + before do + project.add_developer(user) + end + + let!(:schema_inconsistency) do + create(:schema_inconsistency, object_name: 'index_name', table_name: 'achievements', + valitador_name: 'different_definition_indexes') + end + + it 'does not create a schema inconsistency record' do + allow(Gitlab).to receive(:com?).and_return(true) + + expect { execute }.not_to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count } + end + end + end +end diff --git a/spec/models/ci/catalog/resource_spec.rb b/spec/models/ci/catalog/resource_spec.rb index 78656c743ed..1a699fcd842 100644 --- a/spec/models/ci/catalog/resource_spec.rb +++ b/spec/models/ci/catalog/resource_spec.rb @@ -3,6 +3,12 @@ require 'spec_helper' RSpec.describe Ci::Catalog::Resource, feature_category: :pipeline_composition do + it { is_expected.to belong_to(:project) } + + it { is_expected.to delegate_method(:avatar_path).to(:project) } + it { is_expected.to delegate_method(:description).to(:project) } + it { is_expected.to delegate_method(:name).to(:project) } + describe '.for_projects' do it 'returns catalog resources for the given project IDs' do project = create(:project) diff --git a/spec/support/helpers/api_internal_base_helpers.rb b/spec/support/helpers/api_internal_base_helpers.rb index e89716571f9..8299821a699 100644 --- a/spec/support/helpers/api_internal_base_helpers.rb +++ b/spec/support/helpers/api_internal_base_helpers.rb @@ -44,12 +44,14 @@ module APIInternalBaseHelpers end def push(key, container, protocol = 'ssh', env: nil, changes: nil) - push_with_path(key, - full_path: full_path_for(container), - gl_repository: gl_repository_for(container), - protocol: protocol, - env: env, - changes: changes) + push_with_path( + key, + full_path: full_path_for(container), + gl_repository: gl_repository_for(container), + protocol: protocol, + env: env, + changes: changes + ) end def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil) diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb index d7277ba9a20..c7a7993c52b 100644 --- a/spec/support/helpers/board_helpers.rb +++ b/spec/support/helpers/board_helpers.rb @@ -29,13 +29,15 @@ module BoardHelpers # ensure there is enough horizontal space for four board lists resize_window(2000, 800) - drag_to(selector: selector, - scrollable: '#board-app', - list_from_index: list_from_index, - from_index: from_index, - to_index: to_index, - list_to_index: list_to_index, - perform_drop: perform_drop) + drag_to( + selector: selector, + scrollable: '#board-app', + list_from_index: list_from_index, + from_index: from_index, + to_index: to_index, + list_to_index: list_to_index, + perform_drop: perform_drop + ) end wait_for_requests diff --git a/spec/support/helpers/ci/source_pipeline_helpers.rb b/spec/support/helpers/ci/source_pipeline_helpers.rb index b99f499cc16..ef3aea7de52 100644 --- a/spec/support/helpers/ci/source_pipeline_helpers.rb +++ b/spec/support/helpers/ci/source_pipeline_helpers.rb @@ -3,11 +3,13 @@ module Ci module SourcePipelineHelpers def create_source_pipeline(upstream, downstream) - create(:ci_sources_pipeline, - source_job: create(:ci_build, pipeline: upstream), - source_project: upstream.project, - pipeline: downstream, - project: downstream.project) + create( + :ci_sources_pipeline, + source_job: create(:ci_build, pipeline: upstream), + source_project: upstream.project, + pipeline: downstream, + project: downstream.project + ) end end end diff --git a/spec/support/helpers/feature_flag_helpers.rb b/spec/support/helpers/feature_flag_helpers.rb index 5a1c5a6bdab..3cf611c66e6 100644 --- a/spec/support/helpers/feature_flag_helpers.rb +++ b/spec/support/helpers/feature_flag_helpers.rb @@ -2,22 +2,32 @@ module FeatureFlagHelpers def create_flag(project, name, active = true, description: nil, version: Operations::FeatureFlag.versions['new_version_flag']) - create(:operations_feature_flag, name: name, active: active, version: version, - description: description, project: project) + create( + :operations_feature_flag, + name: name, + active: active, + version: version, + description: description, + project: project + ) end def create_scope(feature_flag, environment_scope, active = true, strategies = [{ name: "default", parameters: {} }]) - create(:operations_feature_flag_scope, + create( + :operations_feature_flag_scope, feature_flag: feature_flag, environment_scope: environment_scope, active: active, - strategies: strategies) + strategies: strategies + ) end def create_strategy(feature_flag, name = 'default', parameters = {}) - create(:operations_strategy, + create( + :operations_strategy, feature_flag: feature_flag, - name: name) + name: name + ) end def within_feature_flag_row(index) diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 191e5192a61..a55027d3976 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -89,13 +89,16 @@ module GraphqlHelpers # All mutations accept a single `:input` argument. Wrap arguments here. args = { input: args } if resolver_class <= ::Mutations::BaseMutation && !args.key?(:input) - resolve_field(field, obj, - args: args, - ctx: ctx, - schema: schema, - object_type: resolver_parent, - extras: { parent: parent, lookahead: lookahead }, - arg_style: arg_style) + resolve_field( + field, + obj, + args: args, + ctx: ctx, + schema: schema, + object_type: resolver_parent, + extras: { parent: parent, lookahead: lookahead }, + arg_style: arg_style + ) end # Resolve the value of a field on an object. @@ -513,20 +516,23 @@ module GraphqlHelpers end def post_graphql_mutation(mutation, current_user: nil, token: {}) - post_graphql(mutation.query, - current_user: current_user, - variables: mutation.variables, - token: token) + post_graphql( + mutation.query, + current_user: current_user, + variables: mutation.variables, + token: token + ) end def post_graphql_mutation_with_uploads(mutation, current_user: nil) file_paths = file_paths_in_mutation(mutation) params = mutation_to_apollo_uploads_param(mutation, files: file_paths) - workhorse_post_with_file(api('/', current_user, version: 'graphql'), - params: params, - file_key: '1' - ) + workhorse_post_with_file( + api('/', current_user, version: 'graphql'), + params: params, + file_key: '1' + ) end def file_paths_in_mutation(mutation) diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index 65c0c32c44c..4efe2a98a45 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -2,9 +2,11 @@ module StubObjectStorage def stub_dependency_proxy_object_storage(**params) - stub_object_storage_uploader(config: ::Gitlab.config.dependency_proxy.object_store, - uploader: ::DependencyProxy::FileUploader, - **params) + stub_object_storage_uploader( + config: ::Gitlab.config.dependency_proxy.object_store, + uploader: ::DependencyProxy::FileUploader, + **params + ) end def stub_object_storage_uploader( @@ -36,8 +38,10 @@ module StubObjectStorage return unless enabled - stub_object_storage(connection_params: uploader.object_store_credentials, - remote_directory: old_config.remote_directory) + stub_object_storage( + connection_params: uploader.object_store_credentials, + remote_directory: old_config.remote_directory + ) end def stub_object_storage(connection_params:, remote_directory:) @@ -55,75 +59,99 @@ module StubObjectStorage end def stub_artifacts_object_storage(uploader = JobArtifactUploader, **params) - stub_object_storage_uploader(config: Gitlab.config.artifacts.object_store, - uploader: uploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.artifacts.object_store, + uploader: uploader, + **params + ) end def stub_external_diffs_object_storage(uploader = described_class, **params) - stub_object_storage_uploader(config: Gitlab.config.external_diffs.object_store, - uploader: uploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.external_diffs.object_store, + uploader: uploader, + **params + ) end def stub_lfs_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.lfs.object_store, - uploader: LfsObjectUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.lfs.object_store, + uploader: LfsObjectUploader, + **params + ) end def stub_package_file_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.packages.object_store, - uploader: ::Packages::PackageFileUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.packages.object_store, + uploader: ::Packages::PackageFileUploader, + **params + ) end def stub_rpm_repository_file_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.packages.object_store, - uploader: ::Packages::Rpm::RepositoryFileUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.packages.object_store, + uploader: ::Packages::Rpm::RepositoryFileUploader, + **params + ) end def stub_composer_cache_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.packages.object_store, - uploader: ::Packages::Composer::CacheUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.packages.object_store, + uploader: ::Packages::Composer::CacheUploader, + **params + ) end def debian_component_file_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.packages.object_store, - uploader: ::Packages::Debian::ComponentFileUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.packages.object_store, + uploader: ::Packages::Debian::ComponentFileUploader, + **params + ) end def debian_distribution_release_file_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.packages.object_store, - uploader: ::Packages::Debian::DistributionReleaseFileUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.packages.object_store, + uploader: ::Packages::Debian::DistributionReleaseFileUploader, + **params + ) end def stub_uploads_object_storage(uploader = described_class, **params) - stub_object_storage_uploader(config: Gitlab.config.uploads.object_store, - uploader: uploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.uploads.object_store, + uploader: uploader, + **params + ) end def stub_ci_secure_file_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.ci_secure_files.object_store, - uploader: Ci::SecureFileUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.ci_secure_files.object_store, + uploader: Ci::SecureFileUploader, + **params + ) end def stub_terraform_state_object_storage(**params) - stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store, - uploader: Terraform::StateUploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.terraform_state.object_store, + uploader: Terraform::StateUploader, + **params + ) end def stub_pages_object_storage(uploader = described_class, **params) - stub_object_storage_uploader(config: Gitlab.config.pages.object_store, - uploader: uploader, - **params) + stub_object_storage_uploader( + config: Gitlab.config.pages.object_store, + uploader: uploader, + **params + ) end def stub_object_storage_multipart_init(endpoint, upload_id = "upload_id") diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb index 1b3741e59f3..f3b1d3af501 100644 --- a/spec/support/helpers/workhorse_helpers.rb +++ b/spec/support/helpers/workhorse_helpers.rb @@ -29,31 +29,48 @@ module WorkhorseHelpers # workhorse_form_with_file will transform file_key inside params as if it was disk accelerated by workhorse def workhorse_form_with_file(url, file_key:, params:, method: :post) - workhorse_request_with_file(method, url, - file_key: file_key, - params: params, - env: { 'CONTENT_TYPE' => 'multipart/form-data' }, - send_rewritten_field: true + workhorse_request_with_file( + method, url, + file_key: file_key, + params: params, + env: { 'CONTENT_TYPE' => 'multipart/form-data' }, + send_rewritten_field: true ) end # workhorse_finalize will transform file_key inside params as if it was the finalize call of an inline object storage upload. # note that based on the content of the params it can simulate a disc acceleration or an object storage upload def workhorse_finalize(url, file_key:, params:, method: :post, headers: {}, send_rewritten_field: false) - workhorse_finalize_with_multiple_files(url, method: method, file_keys: file_key, params: params, headers: headers, send_rewritten_field: send_rewritten_field) + workhorse_finalize_with_multiple_files( + url, + method: method, + file_keys: file_key, + params: params, + headers: headers, + send_rewritten_field: send_rewritten_field + ) end def workhorse_finalize_with_multiple_files(url, file_keys:, params:, method: :post, headers: {}, send_rewritten_field: false) - workhorse_request_with_multiple_files(method, url, - file_keys: file_keys, - params: params, - extra_headers: headers, - send_rewritten_field: send_rewritten_field + workhorse_request_with_multiple_files( + method, url, + file_keys: file_keys, + params: params, + extra_headers: headers, + send_rewritten_field: send_rewritten_field ) end def workhorse_request_with_file(method, url, file_key:, params:, send_rewritten_field:, env: {}, extra_headers: {}) - workhorse_request_with_multiple_files(method, url, file_keys: file_key, params: params, env: env, extra_headers: extra_headers, send_rewritten_field: send_rewritten_field) + workhorse_request_with_multiple_files( + method, + url, + file_keys: file_key, + params: params, + env: env, + extra_headers: extra_headers, + send_rewritten_field: send_rewritten_field + ) end def workhorse_request_with_multiple_files(method, url, file_keys:, params:, send_rewritten_field:, env: {}, extra_headers: {}) diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 2058eeef442..d6824bc2cd4 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -1868,7 +1868,6 @@ - './ee/spec/models/merge_requests/external_status_check_spec.rb' - './ee/spec/models/merge_request_spec.rb' - './ee/spec/models/merge_requests/status_check_response_spec.rb' -- './ee/spec/models/merge_train_spec.rb' - './ee/spec/models/milestone_release_spec.rb' - './ee/spec/models/milestone_spec.rb' - './ee/spec/models/namespace_limit_spec.rb' diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb index 63ba5832771..b8fd58e7efa 100644 --- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb +++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb @@ -19,7 +19,7 @@ RSpec.shared_examples 'manage applications' do expect(page).to have_content _('This is the only time the secret is accessible. Copy the secret and store it securely') expect(page).to have_link('Continue', href: index_path) - expect(page).to have_css("button[title=\"Copy secret\"]", text: 'Copy') + expect(page).to have_button(_('Copy secret')) click_on 'Edit' diff --git a/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb index d5ecab0cb6b..ec7a881f7ce 100644 --- a/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb @@ -17,4 +17,10 @@ RSpec.shared_examples "schema objects assertions for" do |stmt_name| expect(schema_object.statement).to eq(statement) end end + + describe '#table_name' do + it 'returns schema object table_name' do + expect(schema_object.table_name).to eq(table_name) + end + end end |