summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-24 21:08:03 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-24 21:08:03 +0000
commit0b5e5c78a9d7acbf773d7ba5fd5c2ce9b6ffdb52 (patch)
tree64e092630aca6e42462003414519f22266dcbc25
parentd48b87d4675d6b8b56dd9b40afa9eb2dce32ad3b (diff)
downloadgitlab-ce-0b5e5c78a9d7acbf773d7ba5fd5c2ce9b6ffdb52.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/merge_request_templates/Revert To Resolve Incident.md1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/projects/compare/components/revision_dropdown.vue2
-rw-r--r--app/controllers/registrations_controller.rb11
-rw-r--r--app/helpers/registrations_helper.rb4
-rw-r--r--app/mailers/emails/work_items.rb15
-rw-r--r--app/mailers/notify.rb1
-rw-r--r--app/mailers/previews/notify_preview.rb4
-rw-r--r--app/services/work_items/import_csv_service.rb14
-rw-r--r--app/views/devise/registrations/new.html.haml2
-rw-r--r--app/views/notify/import_work_items_csv_email.html.haml18
-rw-r--r--app/views/notify/import_work_items_csv_email.text.erb11
-rw-r--r--db/docs/postgres_async_foreign_key_validations.yml2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/development/database/add_foreign_key_to_existing_column.md2
-rw-r--r--doc/user/clusters/agent/gitops.md4
-rw-r--r--doc/user/clusters/agent/index.md16
-rw-r--r--lib/gitlab/database/async_constraints.rb (renamed from lib/gitlab/database/async_foreign_keys.rb)4
-rw-r--r--lib/gitlab/database/async_constraints/foreign_key_validator.rb (renamed from lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb)2
-rw-r--r--lib/gitlab/database/async_constraints/migration_helpers.rb (renamed from lib/gitlab/database/async_foreign_keys/migration_helpers.rb)20
-rw-r--r--lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb (renamed from lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb)8
-rw-r--r--lib/gitlab/database/migration_helpers.rb2
-rw-r--r--lib/gitlab/database/reindexing.rb2
-rw-r--r--lib/tasks/gitlab/db.rake6
-rw-r--r--locale/gitlab.pot14
-rw-r--r--spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb4
-rw-r--r--spec/features/merge_request/user_sees_discussions_navigation_spec.rb2
-rw-r--r--spec/frontend/projects/compare/components/revision_dropdown_spec.js39
-rw-r--r--spec/lib/gitlab/database/async_constraints/foreign_key_validator_spec.rb (renamed from spec/lib/gitlab/database/async_foreign_keys/foreign_key_validator_spec.rb)8
-rw-r--r--spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb (renamed from spec/lib/gitlab/database/async_foreign_keys/migration_helpers_spec.rb)28
-rw-r--r--spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb (renamed from spec/lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation_spec.rb)34
-rw-r--r--spec/lib/gitlab/database/async_constraints_spec.rb (renamed from spec/lib/gitlab/database/async_foreign_keys_spec.rb)6
-rw-r--r--spec/lib/gitlab/database/reindexing_spec.rb4
-rw-r--r--spec/services/work_items/import_csv_service_spec.rb13
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb52
-rw-r--r--spec/views/notify/import_issues_csv_email.html.haml_spec.rb4
-rw-r--r--spec/views/notify/import_work_items_csv_email.html.haml_spec.rb61
37 files changed, 306 insertions, 118 deletions
diff --git a/.gitlab/merge_request_templates/Revert To Resolve Incident.md b/.gitlab/merge_request_templates/Revert To Resolve Incident.md
index 4e77846575a..c1980d70768 100644
--- a/.gitlab/merge_request_templates/Revert To Resolve Incident.md
+++ b/.gitlab/merge_request_templates/Revert To Resolve Incident.md
@@ -12,6 +12,7 @@
- [ ] Create an issue to reinstate the merge request and assign it to the author of the reverted merge request.
- [ ] If the revert is to resolve a [broken 'master' incident](https://about.gitlab.com/handbook/engineering/workflow/#broken-master), please read through the [Responsibilities of the Broken `master` resolution DRI](https://about.gitlab.com/handbook/engineering/workflow/#responsibilities-of-the-resolution-dri).
+- [ ] If the revert involves a database migration, please read through [Deleting existing migrations](https://docs.gitlab.com/ee/development/database/deleting_migrations.html).
- [ ] Add the appropriate labels **before** the MR is created. We can skip CI/CD jobs only if the labels are added **before** the CI/CD pipeline is created.
### Milestone info
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 384ce8b6308..5f0bd4e7c8a 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-fb7ac6faa5b8e8cad4a66e597665eb12d398b84d
+770edd2d7f8324da646df478eb271544393316df
diff --git a/app/assets/javascripts/projects/compare/components/revision_dropdown.vue b/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
index 10531e950f9..92ff06cd579 100644
--- a/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
+++ b/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
@@ -149,6 +149,7 @@ export default {
:key="branch"
is-check-item
:is-checked="selectedRevision === branch"
+ data-testid="branches-dropdown-item"
@click="onClick(branch)"
>
{{ branch }}
@@ -161,6 +162,7 @@ export default {
:key="tag"
is-check-item
:is-checked="selectedRevision === tag"
+ data-testid="tags-dropdown-item"
@click="onClick(tag)"
>
{{ tag }}
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index f9d522f93bb..420ca6a2286 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -133,7 +133,10 @@ class RegistrationsController < Devise::RegistrationsController
# after user confirms and comes back, he will be redirected
store_location_for(:redirect, after_sign_up_path)
- return identity_verification_redirect_path if custom_confirmation_enabled?
+ if custom_confirmation_enabled?
+ session[:verification_user_id] = resource.id # This is needed to find the user on the identity verification page
+ return identity_verification_redirect_path
+ end
Gitlab::Tracking.event(self.class.name, 'render', user: resource)
users_almost_there_path(email: resource.email)
@@ -220,7 +223,7 @@ class RegistrationsController < Devise::RegistrationsController
def resource
@resource ||= Users::RegistrationsBuildService
- .new(current_user, sign_up_params.merge({ skip_confirmation: registered_with_invite_email?,
+ .new(current_user, sign_up_params.merge({ skip_confirmation: skip_confirmation?,
preferred_language: preferred_language }))
.execute
end
@@ -229,6 +232,10 @@ class RegistrationsController < Devise::RegistrationsController
@devise_mapping ||= Devise.mappings[:user]
end
+ def skip_confirmation?
+ registered_with_invite_email?
+ end
+
def registered_with_invite_email?
invite_email = session.delete(:invite_email)
diff --git a/app/helpers/registrations_helper.rb b/app/helpers/registrations_helper.rb
index 1724e11a6f1..a9b7ad7a3e5 100644
--- a/app/helpers/registrations_helper.rb
+++ b/app/helpers/registrations_helper.rb
@@ -14,6 +14,10 @@ module RegistrationsHelper
def arkose_labs_challenge_enabled?
false
end
+
+ def signup_box_template
+ 'devise/shared/signup_box'
+ end
end
RegistrationsHelper.prepend_mod_with('RegistrationsHelper')
diff --git a/app/mailers/emails/work_items.rb b/app/mailers/emails/work_items.rb
new file mode 100644
index 00000000000..fe669fdbedc
--- /dev/null
+++ b/app/mailers/emails/work_items.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Emails
+ module WorkItems
+ def import_work_items_csv_email(user_id, project_id, results)
+ @user = User.find(user_id)
+ @project = Project.find(project_id)
+ @results = results
+
+ email_with_layout(
+ to: @user.notification_email_for(@project),
+ subject: subject('Imported work items'))
+ end
+ end
+end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 28ef6d8d6c6..0465887cdc6 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -25,6 +25,7 @@ class Notify < ApplicationMailer
include Emails::AdminNotification
include Emails::IdentityVerification
include Emails::Imports
+ include Emails::WorkItems
helper TimeboxesHelper
helper MergeRequestsHelper
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 7ed594bf571..0d98c5a176a 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -84,6 +84,10 @@ class NotifyPreview < ActionMailer::Preview
Notify.import_issues_csv_email(user.id, project.id, { success: 3, errors: [5, 6, 7], valid_file: true })
end
+ def import_work_items_csv_email
+ Notify.import_work_items_csv_email(user.id, project.id, { success: 4, error_lines: [2, 3, 4], parse_error: false })
+ end
+
def issues_csv_email
Notify.issues_csv_email(user, project, '1997,Ford,E350', { truncated: false, rows_expected: 3, rows_written: 3 }).message
end
diff --git a/app/services/work_items/import_csv_service.rb b/app/services/work_items/import_csv_service.rb
index e93905b8ca5..4c00d14e9f4 100644
--- a/app/services/work_items/import_csv_service.rb
+++ b/app/services/work_items/import_csv_service.rb
@@ -6,6 +6,10 @@ module WorkItems
NotAvailableError = StandardError.new('This feature is currently behind a feature flag and it is not available.')
+ def self.required_headers
+ %w[title].freeze
+ end
+
def execute
raise NotAvailableError if ::Feature.disabled?(:import_export_work_items_csv, project)
@@ -13,7 +17,7 @@ module WorkItems
end
def email_results_to_user
- # todo as part of https://gitlab.com/gitlab-org/gitlab/-/issues/379153
+ Notify.import_work_items_csv_email(user.id, project.id, results).deliver_later
end
private
@@ -36,15 +40,13 @@ module WorkItems
override :validate_headers_presence!
def validate_headers_presence!(headers)
- headers.downcase! if headers
+ required_headers = self.class.required_headers
+
+ headers.downcase!
return if headers && required_headers.all? { |rh| headers.include?(rh) }
required_headers_message = "Required headers are missing. Required headers are #{required_headers.join(', ')}"
raise CSV::MalformedCSVError.new(required_headers_message, 1)
end
-
- def required_headers
- %w[title].freeze
- end
end
end
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 8a960602536..3bd7147f195 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -8,7 +8,7 @@
= render "layouts/google_tag_manager_body"
.signup-page
- = render 'devise/shared/signup_box',
+ = render signup_box_template,
url: registration_path(resource_name, glm_tracking_params.to_hash),
button_text: _('Register'),
borderless: Feature.enabled?(:restyle_login_page, @project),
diff --git a/app/views/notify/import_work_items_csv_email.html.haml b/app/views/notify/import_work_items_csv_email.html.haml
new file mode 100644
index 00000000000..425cf42fbd0
--- /dev/null
+++ b/app/views/notify/import_work_items_csv_email.html.haml
@@ -0,0 +1,18 @@
+- text_style = 'font-size:16px; text-align:center; line-height:30px;'
+
+%p{ style: text_style }
+ - project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none;")
+ = s_('Notify|Your CSV import of work items for project %{project_link} has been completed.').html_safe % { project_link: project_link }
+
+%p{ style: text_style }
+ - work_items = n_('%d work item', '%d work items', @results[:success]) % @results[:success]
+ = s_('Notify|%{work_items} imported.') % { work_items: work_items }
+
+- if @results[:error_lines].present?
+ %p{ style: text_style }
+ = s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check that these lines have the following fields: %{required_headers}.') % { singular_or_plural_line: n_('line', 'lines', @results[:error_lines].size), required_headers: WorkItems::ImportCsvService.required_headers.join(', '),
+ error_lines: @results[:error_lines].join(', ') }
+
+- if @results[:parse_error]
+ %p{ style: text_style }
+ = s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.')
diff --git a/app/views/notify/import_work_items_csv_email.text.erb b/app/views/notify/import_work_items_csv_email.text.erb
new file mode 100644
index 00000000000..5793131af89
--- /dev/null
+++ b/app/views/notify/import_work_items_csv_email.text.erb
@@ -0,0 +1,11 @@
+Your CSV import for project <%= @project.full_name %> (<%= project_url(@project) %>) has been completed.
+
+<%= pluralize(@results[:success], 'work item') %> imported.
+
+<% if @results[:error_lines].present? %>
+Errors found on line <%= 'number'.pluralize(@results[:error_lines].size) %>: <%= @results[:error_lines].join(', ') %>. Please check that these lines have the following fields: <%= WorkItems::ImportCsvService.required_headers.join(', ') %>.
+<% end %>
+
+<% if @results[:parse_error] %>
+Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.
+<% end %>
diff --git a/db/docs/postgres_async_foreign_key_validations.yml b/db/docs/postgres_async_foreign_key_validations.yml
index 0b9608a3ace..587dd7d69a2 100644
--- a/db/docs/postgres_async_foreign_key_validations.yml
+++ b/db/docs/postgres_async_foreign_key_validations.yml
@@ -1,7 +1,7 @@
---
table_name: postgres_async_foreign_key_validations
classes:
-- Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValidation
+- Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation
feature_categories:
- database
description: >-
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 9d15d4bbb46..28428f1eecc 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -218,7 +218,7 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_lfs_objects_registry` | Gauge | 14.6 | Number of LFS objects in the registry | `url` |
| `geo_lfs_objects_verified` | Gauge | 14.6 | Number of LFS objects verified on secondary | `url` |
| `geo_lfs_objects_verification_failed` | Gauge | 14.6 | Number of LFS objects' verifications failed on secondary | `url` |
-| `geo_lfs_objects_verification_total` | Gauge | 14.6 | Number of LFS objects' verifications tried on secondary | `url` |LFS objects failed to sync on secondary | `url` |
+| `geo_lfs_objects_verification_total` | Gauge | 14.6 | Number of LFS objects' verifications tried on secondary | `url` |
| `geo_attachments` | Gauge | 10.2 | Total number of file attachments available on primary | `url` |
| `geo_attachments_synced` | Gauge | 10.2 | Number of attachments synced on secondary | `url` |
| `geo_attachments_failed` | Gauge | 10.2 | Number of attachments failed to sync on secondary | `url` |
diff --git a/doc/development/database/add_foreign_key_to_existing_column.md b/doc/development/database/add_foreign_key_to_existing_column.md
index a64fc047a57..823fb49a9ab 100644
--- a/doc/development/database/add_foreign_key_to_existing_column.md
+++ b/doc/development/database/add_foreign_key_to_existing_column.md
@@ -252,7 +252,7 @@ validating a foreign key:
1. Enable the feature flag by running `Feature.enable(:database_async_foreign_key_validation)`
in the Rails console.
1. Run `bundle exec rails db:migrate` so that it creates an entry in the async validation table.
-1. Run `bundle exec rails gitlab:db:execute_async_fk_validations:all` so that the FK is validated
+1. Run `bundle exec rails gitlab:db:validate_async_constraints:all` so that the FK is validated
asynchronously on all databases.
1. To verify the foreign key, open the PostgreSQL console using the
[GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/postgresql.md)
diff --git a/doc/user/clusters/agent/gitops.md b/doc/user/clusters/agent/gitops.md
index 787b0062017..1d71f6ac511 100644
--- a/doc/user/clusters/agent/gitops.md
+++ b/doc/user/clusters/agent/gitops.md
@@ -12,6 +12,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/346585) to make the `id` attribute optional in GitLab 15.7.
> - Specifying a branch, tag, or commit reference to fetch the Kubernetes manifest files [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4516) in GitLab 15.7.
+NOTE:
+From GitLab 15.10, you should use [Flux](gitops/flux.md) for GitOps. For more information, see
+[this announcement blog post](https://about.gitlab.com/blog/2023/02/08/why-did-we-choose-to-integrate-fluxcd-with-gitlab/).
+
With GitOps, you can manage containerized clusters and applications from a Git repository that:
- Is the single source of truth of your system.
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 8f4855a7f93..0d84a617808 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -12,6 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3834) in GitLab 13.11, the GitLab agent became available on GitLab.com.
> - [Moved](https://gitlab.com/groups/gitlab-org/-/epics/6290) from GitLab Premium to GitLab Free in 14.5.
> - [Renamed](https://gitlab.com/groups/gitlab-org/-/epics/7167) from "GitLab Kubernetes Agent" to "GitLab agent for Kubernetes" in GitLab 14.6.
+> - Flux [recommended](https://gitlab.com/gitlab-org/gitlab/-/issues/357947#note_1253489000) as GitOps solution in GitLab 15.10.
You can connect your Kubernetes cluster with GitLab to deploy, manage,
and monitor your cloud-native solutions.
@@ -33,20 +34,7 @@ You can choose from two primary workflows. The GitOps workflow is recommended.
### GitOps workflow
-In a [**GitOps** workflow](gitops.md):
-
-- You keep your Kubernetes manifests in GitLab.
-- You install a GitLab agent in your cluster.
-- Any time you update your manifests, the agent updates the cluster.
-- The cluster automatically cleans up unexpected changes. It uses
- [server-side applies](https://kubernetes.io/docs/reference/using-api/server-side-apply/)
- to fix any configuration inconsistencies that third parties introduce.
-
-This workflow is fully driven with Git and is considered **pull-based**,
-because the cluster is pulling updates from your GitLab repository.
-
-GitLab recommends this workflow. We are actively investing in this workflow
-so we can provide a first-class experience.
+You should use Flux for GitOps. To get started, see the GitLab [Flux documentation](../../../user/clusters/agent/gitops/flux.md).
### GitLab CI/CD workflow
diff --git a/lib/gitlab/database/async_foreign_keys.rb b/lib/gitlab/database/async_constraints.rb
index 115ae9ba2e8..c4e05a88430 100644
--- a/lib/gitlab/database/async_foreign_keys.rb
+++ b/lib/gitlab/database/async_constraints.rb
@@ -2,11 +2,11 @@
module Gitlab
module Database
- module AsyncForeignKeys
+ module AsyncConstraints
DEFAULT_ENTRIES_PER_INVOCATION = 2
def self.validate_pending_entries!(how_many: DEFAULT_ENTRIES_PER_INVOCATION)
- PostgresAsyncForeignKeyValidation.ordered.limit(how_many).each do |record|
+ PostgresAsyncConstraintValidation.ordered.limit(how_many).each do |record|
ForeignKeyValidator.new(record).perform
end
end
diff --git a/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb b/lib/gitlab/database/async_constraints/foreign_key_validator.rb
index 1eb8588405d..a535a86913c 100644
--- a/lib/gitlab/database/async_foreign_keys/foreign_key_validator.rb
+++ b/lib/gitlab/database/async_constraints/foreign_key_validator.rb
@@ -2,7 +2,7 @@
module Gitlab
module Database
- module AsyncForeignKeys
+ module AsyncConstraints
class ForeignKeyValidator
include AsyncDdlExclusiveLeaseGuard
diff --git a/lib/gitlab/database/async_foreign_keys/migration_helpers.rb b/lib/gitlab/database/async_constraints/migration_helpers.rb
index eb33b9dc1f6..f51bb015c88 100644
--- a/lib/gitlab/database/async_foreign_keys/migration_helpers.rb
+++ b/lib/gitlab/database/async_constraints/migration_helpers.rb
@@ -2,17 +2,17 @@
module Gitlab
module Database
- module AsyncForeignKeys
+ module AsyncConstraints
module MigrationHelpers
# Prepares a foreign key for asynchronous validation.
#
- # Stores the FK information in the postgres_async_foreign_key_validations
+ # Stores the FK information in the postgres_async_constraint_validations
# table to be executed later.
#
def prepare_async_foreign_key_validation(table_name, column_name = nil, name: nil)
Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_ddl_mode!
- return unless async_fk_validation_available?
+ return unless async_constraint_validation_available?
fk_name = name || concurrent_foreign_key_name(table_name, column_name)
@@ -20,7 +20,7 @@ module Gitlab
raise missing_schema_object_message(table_name, "foreign key", fk_name)
end
- async_validation = PostgresAsyncForeignKeyValidation
+ async_validation = PostgresAsyncConstraintValidation
.find_or_create_by!(name: fk_name, table_name: table_name)
Gitlab::AppLogger.info(
@@ -34,11 +34,11 @@ module Gitlab
def unprepare_async_foreign_key_validation(table_name, column_name = nil, name: nil)
Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_ddl_mode!
- return unless async_fk_validation_available?
+ return unless async_constraint_validation_available?
fk_name = name || concurrent_foreign_key_name(table_name, column_name)
- PostgresAsyncForeignKeyValidation
+ PostgresAsyncConstraintValidation
.find_by(name: fk_name, table_name: table_name)
.try(&:destroy!)
end
@@ -46,7 +46,7 @@ module Gitlab
def prepare_partitioned_async_foreign_key_validation(table_name, column_name = nil, name: nil)
Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_ddl_mode!
- return unless async_fk_validation_available?
+ return unless async_constraint_validation_available?
Gitlab::Database::PostgresPartitionedTable.each_partition(table_name) do |partition|
prepare_async_foreign_key_validation(partition.identifier, column_name, name: name)
@@ -56,7 +56,7 @@ module Gitlab
def unprepare_partitioned_async_foreign_key_validation(table_name, column_name = nil, name: nil)
Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.require_ddl_mode!
- return unless async_fk_validation_available?
+ return unless async_constraint_validation_available?
Gitlab::Database::PostgresPartitionedTable.each_partition(table_name) do |partition|
unprepare_async_foreign_key_validation(partition.identifier, column_name, name: name)
@@ -65,8 +65,8 @@ module Gitlab
private
- def async_fk_validation_available?
- connection.table_exists?(:postgres_async_foreign_key_validations)
+ def async_constraint_validation_available?
+ PostgresAsyncConstraintValidation.table_available?
end
end
end
diff --git a/lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb b/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb
index fb01c1e2025..ae996600f7c 100644
--- a/lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb
+++ b/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb
@@ -2,8 +2,8 @@
module Gitlab
module Database
- module AsyncForeignKeys
- class PostgresAsyncForeignKeyValidation < SharedModel
+ module AsyncConstraints
+ class PostgresAsyncConstraintValidation < SharedModel
include QueueErrorHandlingConcern
self.table_name = 'postgres_async_foreign_key_validations'
@@ -15,6 +15,10 @@ module Gitlab
validates :table_name, presence: true, length: { maximum: MAX_IDENTIFIER_LENGTH }
scope :ordered, -> { order(attempts: :asc, id: :asc) }
+
+ def self.table_available?
+ connection.table_exists?(table_name)
+ end
end
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 08122875abc..271da2edbc9 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -14,7 +14,7 @@ module Gitlab
include DynamicModelHelpers
include RenameTableHelpers
include AsyncIndexes::MigrationHelpers
- include AsyncForeignKeys::MigrationHelpers
+ include AsyncConstraints::MigrationHelpers
def define_batchable_model(table_name, connection: self.connection)
super(table_name, connection: connection)
diff --git a/lib/gitlab/database/reindexing.rb b/lib/gitlab/database/reindexing.rb
index 78de7161a0f..739e573b6c4 100644
--- a/lib/gitlab/database/reindexing.rb
+++ b/lib/gitlab/database/reindexing.rb
@@ -28,7 +28,7 @@ module Gitlab
# Hack: Before we do actual reindexing work, create async indexes
Gitlab::Database::AsyncIndexes.create_pending_indexes! if Feature.enabled?(:database_async_index_creation, type: :ops)
Gitlab::Database::AsyncIndexes.drop_pending_indexes!
- Gitlab::Database::AsyncForeignKeys.validate_pending_entries! if Feature.enabled?(:database_async_foreign_key_validation, type: :ops)
+ Gitlab::Database::AsyncConstraints.validate_pending_entries! if Feature.enabled?(:database_async_foreign_key_validation, type: :ops)
automatic_reindexing
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 14d66816551..5a67e6cfa8c 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -300,7 +300,7 @@ namespace :gitlab do
end
end
- namespace :execute_async_fk_validations do
+ namespace :validate_async_constraints do
each_database(databases) do |database_name|
task database_name, [:pick] => :environment do |_, args|
args.with_defaults(pick: 2)
@@ -315,7 +315,7 @@ namespace :gitlab do
end
Gitlab::Database::EachDatabase.each_database_connection(only: database_name) do
- Gitlab::Database::AsyncForeignKeys.validate_pending_entries!(how_many: args[:pick].to_i)
+ Gitlab::Database::AsyncConstraints.validate_pending_entries!(how_many: args[:pick].to_i)
end
end
end
@@ -325,7 +325,7 @@ namespace :gitlab do
args.with_defaults(pick: default_pick)
each_database(databases) do |database_name|
- Rake::Task["gitlab:db:execute_async_fk_validations:#{database_name}"].invoke(args[:pick])
+ Rake::Task["gitlab:db:validate_async_constraints:#{database_name}"].invoke(args[:pick])
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 800abad7e33..b1c9c318e54 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -466,6 +466,11 @@ msgid_plural "%d warnings found:"
msgstr[0] ""
msgstr[1] ""
+msgid "%d work item"
+msgid_plural "%d work items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
@@ -29011,6 +29016,9 @@ msgstr ""
msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
msgstr ""
+msgid "Notify|%{work_items} imported."
+msgstr ""
+
msgid "Notify|A new GPG key was added to your account:"
msgstr ""
@@ -29059,6 +29067,9 @@ msgstr ""
msgid "Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title."
msgstr ""
+msgid "Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check that these lines have the following fields: %{required_headers}."
+msgstr ""
+
msgid "Notify|Fingerprint: %{fingerprint}"
msgstr ""
@@ -29197,6 +29208,9 @@ msgstr ""
msgid "Notify|Your CSV import for project %{project_link} has been completed."
msgstr ""
+msgid "Notify|Your CSV import of work items for project %{project_link} has been completed."
+msgstr ""
+
msgid "Notify|Your account has been created successfully."
msgstr ""
diff --git a/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb b/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb
index a61b5cde7a0..23f4049f1cb 100644
--- a/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb
+++ b/spec/factories/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :postgres_async_foreign_key_validation,
- class: 'Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValidation' do
+ factory :postgres_async_constraint_validation,
+ class: 'Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation' do
sequence(:name) { |n| "fk_users_id_#{n}" }
table_name { "users" }
end
diff --git a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
index 6e6c2cddfbf..06276d2a933 100644
--- a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe 'Merge request > User sees discussions navigation', :js, feature_
expect(page).to have_selector(second_discussion_selector, obscured: false)
end
- it 'navigates through active threads' do
+ it 'navigates through active threads', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391912' do
goto_next_thread
goto_next_thread
expect(page).to have_selector(second_discussion_selector, obscured: false)
diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
index db4a1158996..50779063d6b 100644
--- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js
+++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
@@ -2,6 +2,7 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
@@ -38,6 +39,10 @@ describe('RevisionDropdown component', () => {
const findGlDropdown = () => wrapper.findComponent(GlDropdown);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findBranchesDropdownItem = () =>
+ wrapper.findAllComponents('[data-testid="branches-dropdown-item"]');
+ const findTagsDropdownItem = () =>
+ wrapper.findAllComponents('[data-testid="tags-dropdown-item"]');
it('sets hidden input', () => {
createComponent();
@@ -57,17 +62,29 @@ describe('RevisionDropdown component', () => {
createComponent();
- await axios.waitForAll();
- expect(wrapper.vm.branches).toEqual(Branches);
- expect(wrapper.vm.tags).toEqual(Tags);
+ expect(findBranchesDropdownItem()).toHaveLength(0);
+ expect(findTagsDropdownItem()).toHaveLength(0);
+
+ await waitForPromises();
+
+ expect(findBranchesDropdownItem()).toHaveLength(Branches.length);
+ expect(findTagsDropdownItem()).toHaveLength(Tags.length);
+
+ Branches.forEach((branch, index) => {
+ expect(findBranchesDropdownItem().at(index).text()).toBe(branch);
+ });
+
+ Tags.forEach((tag, index) => {
+ expect(findTagsDropdownItem().at(index).text()).toBe(tag);
+ });
});
it('shows flash message on error', async () => {
axiosMock.onGet('some/invalid/path').replyOnce(HTTP_STATUS_NOT_FOUND);
createComponent();
+ await waitForPromises();
- await wrapper.vm.fetchBranchesAndTags();
expect(createAlert).toHaveBeenCalled();
});
@@ -83,7 +100,7 @@ describe('RevisionDropdown component', () => {
refsProjectPath: newRefsProjectPath,
});
- await axios.waitForAll();
+ await waitForPromises();
expect(axios.get).toHaveBeenLastCalledWith(newRefsProjectPath);
});
@@ -92,8 +109,8 @@ describe('RevisionDropdown component', () => {
axiosMock.onGet('some/invalid/path').replyOnce(HTTP_STATUS_NOT_FOUND);
createComponent();
+ await waitForPromises();
- await wrapper.vm.searchBranchesAndTags();
expect(createAlert).toHaveBeenCalled();
});
@@ -108,7 +125,7 @@ describe('RevisionDropdown component', () => {
const mockSearchTerm = 'foobar';
createComponent();
findSearchBox().vm.$emit('input', mockSearchTerm);
- await axios.waitForAll();
+ await waitForPromises();
expect(axios.get).toHaveBeenCalledWith(
defaultProps.refsProjectPath,
@@ -141,8 +158,14 @@ describe('RevisionDropdown component', () => {
});
it('emits `selectRevision` event when another revision is selected', async () => {
+ jest.spyOn(axios, 'get').mockResolvedValue({
+ data: {
+ Branches: ['some-branch'],
+ Tags: [],
+ },
+ });
+
createComponent();
- wrapper.vm.branches = ['some-branch'];
await nextTick();
findGlDropdown().findAllComponents(GlDropdownItem).at(0).vm.$emit('click');
diff --git a/spec/lib/gitlab/database/async_foreign_keys/foreign_key_validator_spec.rb b/spec/lib/gitlab/database/async_constraints/foreign_key_validator_spec.rb
index 90137e259f5..15474912d9a 100644
--- a/spec/lib/gitlab/database/async_foreign_keys/foreign_key_validator_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints/foreign_key_validator_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::AsyncForeignKeys::ForeignKeyValidator, feature_category: :database do
+RSpec.describe Gitlab::Database::AsyncConstraints::ForeignKeyValidator, feature_category: :database do
include ExclusiveLeaseHelpers
describe '#perform' do
@@ -10,10 +10,10 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::ForeignKeyValidator, feature_
let(:lease_key) { "gitlab/database/asyncddl/actions/#{Gitlab::Database::PRIMARY_DATABASE_NAME}" }
let(:lease_timeout) { described_class::TIMEOUT_PER_ACTION }
- let(:fk_model) { Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValidation }
+ let(:fk_model) { Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation }
let(:table_name) { '_test_async_fks' }
let(:fk_name) { 'fk_parent_id' }
- let(:validation) { create(:postgres_async_foreign_key_validation, table_name: table_name, name: fk_name) }
+ let(:validation) { create(:postgres_async_constraint_validation, table_name: table_name, name: fk_name) }
let(:connection) { validation.connection }
subject { described_class.new(validation) }
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::ForeignKeyValidator, feature_
context 'with fully qualified table names' do
let(:validation) do
- create(:postgres_async_foreign_key_validation,
+ create(:postgres_async_constraint_validation,
table_name: "public.#{table_name}",
name: fk_name
)
diff --git a/spec/lib/gitlab/database/async_foreign_keys/migration_helpers_spec.rb b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb
index 0bd0e8045ff..ab06c7c7e82 100644
--- a/spec/lib/gitlab/database/async_foreign_keys/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb
@@ -2,10 +2,10 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_category: :database do
+RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_category: :database do
let(:migration) { Gitlab::Database::Migration[2.1].new }
let(:connection) { ApplicationRecord.connection }
- let(:fk_model) { Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValidation }
+ let(:constraint_model) { Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation }
let(:table_name) { '_test_async_fks' }
let(:column_name) { 'parent_id' }
let(:fk_name) { nil }
@@ -28,9 +28,9 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_cat
it 'creates the record for the async FK validation' do
expect do
migration.prepare_async_foreign_key_validation(table_name, column_name)
- end.to change { fk_model.where(table_name: table_name).count }.by(1)
+ end.to change { constraint_model.where(table_name: table_name).count }.by(1)
- record = fk_model.find_by(table_name: table_name)
+ record = constraint_model.find_by(table_name: table_name)
expect(record.name).to start_with('fk_')
end
@@ -41,9 +41,9 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_cat
it 'creates the record with the given name' do
expect do
migration.prepare_async_foreign_key_validation(table_name, name: fk_name)
- end.to change { fk_model.where(name: fk_name).count }.by(1)
+ end.to change { constraint_model.where(name: fk_name).count }.by(1)
- record = fk_model.find_by(name: fk_name)
+ record = constraint_model.find_by(name: fk_name)
expect(record.table_name).to eq(table_name)
end
@@ -61,19 +61,19 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_cat
let(:fk_name) { 'my_fk_name' }
it 'does attempt to create the record' do
- create(:postgres_async_foreign_key_validation, table_name: table_name, name: fk_name)
+ create(:postgres_async_constraint_validation, table_name: table_name, name: fk_name)
expect do
migration.prepare_async_foreign_key_validation(table_name, name: fk_name)
- end.not_to change { fk_model.where(name: fk_name).count }
+ end.not_to change { constraint_model.where(name: fk_name).count }
end
end
context 'when the async FK validation table does not exist' do
it 'does not raise an error' do
- connection.drop_table(:postgres_async_foreign_key_validations)
+ connection.drop_table(constraint_model.table_name)
- expect(fk_model).not_to receive(:safe_find_or_create_by!)
+ expect(constraint_model).not_to receive(:safe_find_or_create_by!)
expect { migration.prepare_async_foreign_key_validation(table_name, column_name) }.not_to raise_error
end
@@ -88,7 +88,7 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_cat
it 'destroys the record' do
expect do
migration.unprepare_async_foreign_key_validation(table_name, column_name)
- end.to change { fk_model.where(table_name: table_name).count }.by(-1)
+ end.to change { constraint_model.where(table_name: table_name).count }.by(-1)
end
context 'when an explicit name is given' do
@@ -97,15 +97,15 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::MigrationHelpers, feature_cat
it 'destroys the record' do
expect do
migration.unprepare_async_foreign_key_validation(table_name, name: fk_name)
- end.to change { fk_model.where(name: fk_name).count }.by(-1)
+ end.to change { constraint_model.where(name: fk_name).count }.by(-1)
end
end
context 'when the async fk validation table does not exist' do
it 'does not raise an error' do
- connection.drop_table(:postgres_async_foreign_key_validations)
+ connection.drop_table(constraint_model.table_name)
- expect(fk_model).not_to receive(:find_by)
+ expect(constraint_model).not_to receive(:find_by)
expect { migration.unprepare_async_foreign_key_validation(table_name, column_name) }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation_spec.rb b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb
index 40ab9cb2dd2..a8e136dd22c 100644
--- a/spec/lib/gitlab/database/async_foreign_keys/postgres_async_foreign_key_validation_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValidation, type: :model,
+RSpec.describe Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation, type: :model,
feature_category: :database do
it { is_expected.to be_a Gitlab::Database::SharedModel }
describe 'validations' do
- let_it_be(:fk_validation) { create(:postgres_async_foreign_key_validation) }
+ let_it_be(:constraint_validation) { create(:postgres_async_constraint_validation) }
let(:identifier_limit) { described_class::MAX_IDENTIFIER_LENGTH }
let(:last_error_limit) { described_class::MAX_LAST_ERROR_LENGTH }
- subject { fk_validation }
+ subject { constraint_validation }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:table_name) }
@@ -22,8 +22,8 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValida
end
describe 'scopes' do
- let!(:failed_validation) { create(:postgres_async_foreign_key_validation, attempts: 1) }
- let!(:new_validation) { create(:postgres_async_foreign_key_validation) }
+ let!(:failed_validation) { create(:postgres_async_constraint_validation, attempts: 1) }
+ let!(:new_validation) { create(:postgres_async_constraint_validation) }
describe '.ordered' do
subject { described_class.ordered }
@@ -32,21 +32,37 @@ RSpec.describe Gitlab::Database::AsyncForeignKeys::PostgresAsyncForeignKeyValida
end
end
+ describe '.table_available?' do
+ subject { described_class.table_available? }
+
+ it { is_expected.to be_truthy }
+
+ context 'when the table does not exist' do
+ before do
+ described_class
+ .connection
+ .drop_table(described_class.table_name)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
describe '#handle_exception!' do
- let_it_be_with_reload(:fk_validation) { create(:postgres_async_foreign_key_validation) }
+ let_it_be_with_reload(:constraint_validation) { create(:postgres_async_constraint_validation) }
let(:error) { instance_double(StandardError, message: 'Oups', backtrace: %w[this that]) }
- subject { fk_validation.handle_exception!(error) }
+ subject { constraint_validation.handle_exception!(error) }
it 'increases the attempts number' do
- expect { subject }.to change { fk_validation.reload.attempts }.by(1)
+ expect { subject }.to change { constraint_validation.reload.attempts }.by(1)
end
it 'saves error details' do
subject
- expect(fk_validation.reload.last_error).to eq("Oups\nthis\nthat")
+ expect(constraint_validation.reload.last_error).to eq("Oups\nthis\nthat")
end
end
end
diff --git a/spec/lib/gitlab/database/async_foreign_keys_spec.rb b/spec/lib/gitlab/database/async_constraints_spec.rb
index f15eb364929..2141131479d 100644
--- a/spec/lib/gitlab/database/async_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/async_constraints_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::AsyncForeignKeys, feature_category: :database do
+RSpec.describe Gitlab::Database::AsyncConstraints, feature_category: :database do
describe '.validate_pending_entries!' do
subject { described_class.validate_pending_entries! }
before do
- create_list(:postgres_async_foreign_key_validation, 3)
+ create_list(:postgres_async_constraint_validation, 3)
end
it 'takes 2 pending FK validations and executes them' do
- validations = described_class::PostgresAsyncForeignKeyValidation.ordered.limit(2).to_a
+ validations = described_class::PostgresAsyncConstraintValidation.ordered.limit(2).to_a
expect_next_instances_of(described_class::ForeignKeyValidator, 2, validations) do |validator|
expect(validator).to receive(:perform)
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index a8af9bb5a38..4d0e58b0937 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::Database::Reindexing, feature_category: :database, time_t
context 'when async FK validation is enabled' do
it 'executes FK validation for each database prior to any reindexing actions' do
- expect(Gitlab::Database::AsyncForeignKeys).to receive(:validate_pending_entries!).ordered.exactly(databases_count).times
+ expect(Gitlab::Database::AsyncConstraints).to receive(:validate_pending_entries!).ordered.exactly(databases_count).times
expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
described_class.invoke
@@ -82,7 +82,7 @@ RSpec.describe Gitlab::Database::Reindexing, feature_category: :database, time_t
it 'does not execute FK validation' do
stub_feature_flags(database_async_foreign_key_validation: false)
- expect(Gitlab::Database::AsyncForeignKeys).not_to receive(:validate_pending_entries!)
+ expect(Gitlab::Database::AsyncConstraints).not_to receive(:validate_pending_entries!)
described_class.invoke
end
diff --git a/spec/services/work_items/import_csv_service_spec.rb b/spec/services/work_items/import_csv_service_spec.rb
index 658062ecdbe..83530c87c99 100644
--- a/spec/services/work_items/import_csv_service_spec.rb
+++ b/spec/services/work_items/import_csv_service_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe WorkItems::ImportCsvService, feature_category: :team_planning do
let_it_be(:issue_type) { ::WorkItems::Type.default_issue_type }
let(:work_items) { ::WorkItems::WorkItemsFinder.new(user, project: project).execute }
+ let(:email_method) { :import_work_items_csv_email }
subject { service.execute }
@@ -26,6 +27,8 @@ RSpec.describe WorkItems::ImportCsvService, feature_category: :team_planning do
project.add_guest(user)
end
+ it_behaves_like 'importer with email notification'
+
context 'when file is valid' do
it 'creates the expected number of work items' do
expect { subject }.to change { work_items.count }.by 2
@@ -96,4 +99,14 @@ RSpec.describe WorkItems::ImportCsvService, feature_category: :team_planning do
end
end
end
+
+ context 'when user does not have permission' do
+ it 'errors on those lines', :aggregate_failures do
+ result = subject
+
+ expect(result[:success]).to eq(0)
+ expect(result[:error_lines]).to eq([2, 3])
+ expect(result[:parse_error]).to eq(false)
+ end
+ end
end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index ddfdf50a31b..f43106959b0 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -841,65 +841,65 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout, feature_categor
end
end
- describe 'execute_async_fk_validations' do
+ describe 'validate_async_constraints' do
before do
skip_if_multiple_databases_not_setup
end
- it 'delegates ci task to Gitlab::Database::AsyncForeignKeys' do
- expect(Gitlab::Database::AsyncForeignKeys).to receive(:validate_pending_entries!).with(how_many: 2)
+ it 'delegates ci task to Gitlab::Database::AsyncConstraints' do
+ expect(Gitlab::Database::AsyncConstraints).to receive(:validate_pending_entries!).with(how_many: 2)
- run_rake_task('gitlab:db:execute_async_fk_validations:ci')
+ run_rake_task('gitlab:db:validate_async_constraints:ci')
end
- it 'delegates ci task to Gitlab::Database::AsyncForeignKeys with specified argument' do
- expect(Gitlab::Database::AsyncForeignKeys).to receive(:validate_pending_entries!).with(how_many: 5)
+ it 'delegates ci task to Gitlab::Database::AsyncConstraints with specified argument' do
+ expect(Gitlab::Database::AsyncConstraints).to receive(:validate_pending_entries!).with(how_many: 5)
- run_rake_task('gitlab:db:execute_async_fk_validations:ci', '[5]')
+ run_rake_task('gitlab:db:validate_async_constraints:ci', '[5]')
end
- it 'delegates main task to Gitlab::Database::AsyncForeignKeys' do
- expect(Gitlab::Database::AsyncForeignKeys).to receive(:validate_pending_entries!).with(how_many: 2)
+ it 'delegates main task to Gitlab::Database::AsyncConstraints' do
+ expect(Gitlab::Database::AsyncConstraints).to receive(:validate_pending_entries!).with(how_many: 2)
- run_rake_task('gitlab:db:execute_async_fk_validations:main')
+ run_rake_task('gitlab:db:validate_async_constraints:main')
end
- it 'delegates main task to Gitlab::Database::AsyncForeignKeys with specified argument' do
- expect(Gitlab::Database::AsyncForeignKeys).to receive(:validate_pending_entries!).with(how_many: 7)
+ it 'delegates main task to Gitlab::Database::AsyncConstraints with specified argument' do
+ expect(Gitlab::Database::AsyncConstraints).to receive(:validate_pending_entries!).with(how_many: 7)
- run_rake_task('gitlab:db:execute_async_fk_validations:main', '[7]')
+ run_rake_task('gitlab:db:validate_async_constraints:main', '[7]')
end
it 'delegates all task to every database with higher default for dev' do
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:ci']).to receive(:invoke).with(1000)
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:main']).to receive(:invoke).with(1000)
+ expect(Rake::Task['gitlab:db:validate_async_constraints:ci']).to receive(:invoke).with(1000)
+ expect(Rake::Task['gitlab:db:validate_async_constraints:main']).to receive(:invoke).with(1000)
- run_rake_task('gitlab:db:execute_async_fk_validations:all')
+ run_rake_task('gitlab:db:validate_async_constraints:all')
end
it 'delegates all task to every database with lower default for prod' do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:ci']).to receive(:invoke).with(2)
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:main']).to receive(:invoke).with(2)
+ expect(Rake::Task['gitlab:db:validate_async_constraints:ci']).to receive(:invoke).with(2)
+ expect(Rake::Task['gitlab:db:validate_async_constraints:main']).to receive(:invoke).with(2)
- run_rake_task('gitlab:db:execute_async_fk_validations:all')
+ run_rake_task('gitlab:db:validate_async_constraints:all')
end
it 'delegates all task to every database with specified argument' do
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:ci']).to receive(:invoke).with('50')
- expect(Rake::Task['gitlab:db:execute_async_fk_validations:main']).to receive(:invoke).with('50')
+ expect(Rake::Task['gitlab:db:validate_async_constraints:ci']).to receive(:invoke).with('50')
+ expect(Rake::Task['gitlab:db:validate_async_constraints:main']).to receive(:invoke).with('50')
- run_rake_task('gitlab:db:execute_async_fk_validations:all', '[50]')
+ run_rake_task('gitlab:db:validate_async_constraints:all', '[50]')
end
context 'when feature is not enabled' do
it 'is a no-op' do
stub_feature_flags(database_async_foreign_key_validation: false)
- expect(Gitlab::Database::AsyncForeignKeys).not_to receive(:validate_pending_entries!)
+ expect(Gitlab::Database::AsyncConstraints).not_to receive(:validate_pending_entries!)
- expect { run_rake_task('gitlab:db:execute_async_fk_validations:main') }.to raise_error(SystemExit)
+ expect { run_rake_task('gitlab:db:validate_async_constraints:main') }.to raise_error(SystemExit)
end
end
@@ -909,8 +909,8 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout, feature_categor
end
it 'does not create a task for the geo database' do
- expect { run_rake_task('gitlab:db:execute_async_fk_validations:geo') }
- .to raise_error(/Don't know how to build task 'gitlab:db:execute_async_fk_validations:geo'/)
+ expect { run_rake_task('gitlab:db:validate_async_constraints:geo') }
+ .to raise_error(/Don't know how to build task 'gitlab:db:validate_async_constraints:geo'/)
end
end
end
diff --git a/spec/views/notify/import_issues_csv_email.html.haml_spec.rb b/spec/views/notify/import_issues_csv_email.html.haml_spec.rb
index 43dfab87ac9..c3d320a837b 100644
--- a/spec/views/notify/import_issues_csv_email.html.haml_spec.rb
+++ b/spec/views/notify/import_issues_csv_email.html.haml_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'notify/import_issues_csv_email.html.haml' do
let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:correct_results) { { success: 3, valid_file: true } }
- let(:errored_results) { { success: 3, error_lines: [5, 6, 7], valid_file: true } }
+ let(:correct_results) { { success: 3, parse_error: false } }
+ let(:errored_results) { { success: 3, error_lines: [5, 6, 7], parse_error: false } }
let(:parse_error_results) { { success: 0, parse_error: true } }
before do
diff --git a/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb b/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb
new file mode 100644
index 00000000000..8a01c11646a
--- /dev/null
+++ b/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'notify/import_work_items_csv_email.html.haml', feature_category: :team_planning do
+ let_it_be(:user) { create(:user) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+ let_it_be(:project) { create(:project) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
+ let_it_be(:correct_results) { { success: 3, parse_error: false } }
+ let_it_be(:errored_results) { { success: 3, error_lines: [5, 6, 7], parse_error: false } }
+ let_it_be(:parse_error_results) { { success: 0, parse_error: true } }
+
+ before do
+ assign(:user, user)
+ assign(:project, project)
+ end
+
+ context 'when no errors found while importing' do
+ before do
+ assign(:results, correct_results)
+ end
+
+ it 'renders correctly' do
+ render
+
+ expect(rendered).to have_link(project.full_name, href: project_url(project))
+ expect(rendered).to have_content("3 work items imported")
+ expect(rendered).not_to have_content("Errors found on line")
+ expect(rendered).not_to have_content(
+ "Error parsing CSV file. Please make sure it has the correct format: \
+a delimited text file that uses a comma to separate values.")
+ end
+ end
+
+ context 'when import errors reported' do
+ before do
+ assign(:results, errored_results)
+ end
+
+ it 'renders correctly' do
+ render
+
+ expect(rendered).to have_content("Errors found on lines: #{errored_results[:error_lines].join(', ')}. \
+Please check that these lines have the following fields: title")
+ expect(rendered).not_to have_content("Error parsing CSV file. Please make sure it has the correct format: \
+a delimited text file that uses a comma to separate values.")
+ end
+ end
+
+ context 'when parse error reported while importing' do
+ before do
+ assign(:results, parse_error_results)
+ end
+
+ it 'renders with parse error' do
+ render
+
+ expect(rendered).to have_content("Error parsing CSV file. \
+Please make sure it has the correct format: a delimited text file that uses a comma to separate values.")
+ end
+ end
+end