From 023c40964548821664b2aa396b58c3f51ea30cc3 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 2 Nov 2022 00:11:19 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/dev-fixtures.gitlab-ci.yml | 2 +- .gitlab/ci/global.gitlab-ci.yml | 28 ++-- .gitlab/ci/rails.gitlab-ci.yml | 70 +++++++--- .gitlab/ci/rails/shared.gitlab-ci.yml | 18 ++- .gitlab/ci/rules.gitlab-ci.yml | 2 + .../components/alert_details.vue | 3 +- .../vue_shared/alert_details/router.js | 19 ++- .../gitlab_rails_cheat_sheet.md | 122 ------------------ doc/development/pipelines/index.md | 2 +- doc/security/two_factor_authentication.md | 5 + doc/subscriptions/self_managed/index.md | 37 ++++++ doc/user/admin_area/moderate_users.md | 59 +++++++++ doc/user/group/manage.md | 14 ++ doc/user/profile/account/create_accounts.md | 13 ++ lib/api/api.rb | 2 +- lib/api/bulk_imports.rb | 48 ++++++- lib/api/entities/bulk_import.rb | 12 +- lib/api/entities/bulk_imports/entity.rb | 28 ++-- .../entities/bulk_imports/entity_failure.rb | 18 +-- .../vue_shared/alert_details/router_spec.js | 35 +++++ 20 files changed, 352 insertions(+), 185 deletions(-) create mode 100644 spec/frontend/vue_shared/alert_details/router_spec.js diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml index 21eae3f23e9..ea868ada621 100644 --- a/.gitlab/ci/dev-fixtures.gitlab-ci.yml +++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml @@ -29,7 +29,7 @@ run-dev-fixtures-ee: extends: - .run-dev-fixtures - .dev-fixtures:rules:ee-only - - .use-pg12-ee + - .use-pg12-es7-ee script: - cp ee/db/fixtures/development/* $FIXTURE_PATH - *run-dev-fixtures-script diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index ed59a0dd8fe..c4cd6cb611d 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -275,34 +275,34 @@ POSTGRES_HOST_AUTH_METHOD: trust PG_VERSION: "13" -.use-pg11-ee: +.use-pg11-es7-ee: services: - name: postgres:11.6 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:5.0-alpine - - name: elasticsearch:7.17.0 + - name: elasticsearch:7.17.6 command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"] variables: POSTGRES_HOST_AUTH_METHOD: trust PG_VERSION: "11" -.use-pg12-ee: +.use-pg12-es7-ee: services: - name: postgres:12 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:6.0-alpine - - name: elasticsearch:7.17.0 + - name: elasticsearch:7.17.6 command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"] variables: POSTGRES_HOST_AUTH_METHOD: trust PG_VERSION: "12" -.use-pg13-ee: +.use-pg13-es7-ee: services: - name: postgres:13 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:6.2-alpine - - name: elasticsearch:7.17.0 + - name: elasticsearch:7.17.6 command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"] variables: POSTGRES_HOST_AUTH_METHOD: trust @@ -313,7 +313,7 @@ - name: postgres:12 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:6.0-alpine - - name: elasticsearch:8.3.3 + - name: elasticsearch:8.4.1 variables: POSTGRES_HOST_AUTH_METHOD: trust PG_VERSION: "12" @@ -325,7 +325,19 @@ - name: postgres:12 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:6.0-alpine - - name: opensearchproject/opensearch:1.2.4 + - name: opensearchproject/opensearch:1.3.5 + alias: elasticsearch + command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"] + variables: + POSTGRES_HOST_AUTH_METHOD: trust + PG_VERSION: "12" + +.use-pg12-opensearch2-ee: + services: + - name: postgres:12 + command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] + - name: redis:6.0-alpine + - name: opensearchproject/opensearch:2.2.1 alias: elasticsearch command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"] variables: diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index c60f85634b6..f4f832b84d0 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -574,11 +574,6 @@ rspec-ee unit pg12 es8: - .rspec-ee-base-pg12-es8 - .rspec-ee-unit-parallel -rspec-ee unit pg12 opensearch1: - extends: - - .rspec-ee-base-pg12-opensearch1 - - .rspec-ee-unit-parallel - rspec-ee unit pg12 minimal: extends: - rspec-ee unit pg12 @@ -602,11 +597,6 @@ rspec-ee integration pg12 es8: - .rspec-ee-base-pg12-es8 - .rspec-ee-integration-parallel -rspec-ee integration pg12 opensearch1: - extends: - - .rspec-ee-base-pg12-opensearch1 - - .rspec-ee-integration-parallel - rspec-ee integration pg12 minimal: extends: - rspec-ee integration pg12 @@ -630,11 +620,6 @@ rspec-ee system pg12 es8: - .rspec-ee-base-pg12-es8 - .rspec-ee-system-parallel -rspec-ee system pg12 opensearch1: - extends: - - .rspec-ee-base-pg12-opensearch1 - - .rspec-ee-system-parallel - rspec-ee system pg12 minimal: extends: - rspec-ee system pg12 @@ -743,6 +728,61 @@ rspec-ee system pg11: - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - .rspec-ee-system-parallel +# PG12 +rspec-ee unit pg12 es7: + extends: + - .rspec-ee-base-pg12-es7 + - .rspec-ee-unit-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee unit pg12 opensearch1: + extends: + - .rspec-ee-base-pg12-opensearch1 + - .rspec-ee-unit-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee unit pg12 opensearch2: + extends: + - .rspec-ee-base-pg12-opensearch2 + - .rspec-ee-unit-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee integration pg12 es7: + extends: + - .rspec-ee-base-pg12-es7 + - .rspec-ee-integration-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee integration pg12 opensearch1: + extends: + - .rspec-ee-base-pg12-opensearch1 + - .rspec-ee-integration-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee integration pg12 opensearch2: + extends: + - .rspec-ee-base-pg12-opensearch2 + - .rspec-ee-integration-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee system pg12 es7: + extends: + - .rspec-ee-base-pg12-es7 + - .rspec-ee-system-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee system pg12 opensearch1: + extends: + - .rspec-ee-base-pg12-opensearch1 + - .rspec-ee-system-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + +rspec-ee system pg12 opensearch2: + extends: + - .rspec-ee-base-pg12-opensearch2 + - .rspec-ee-system-parallel + - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only + # PG13 rspec-ee migration pg13: extends: diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml index 60c9826abfe..d47bac5e433 100644 --- a/.gitlab/ci/rails/shared.gitlab-ci.yml +++ b/.gitlab/ci/rails/shared.gitlab-ci.yml @@ -109,12 +109,18 @@ include: .rspec-ee-base-pg11: extends: - .rspec-base - - .use-pg11-ee + - .use-pg11-es7-ee .rspec-ee-base-pg12: extends: - .rspec-base - - .use-pg12-ee + - .use-pg12-es7-ee + +.rspec-ee-base-pg12-es7: + extends: + - .rspec-base + - .use-pg12-es7-ee + - .rails:rules:run-search-tests .rspec-ee-base-pg12-es8: extends: @@ -128,10 +134,16 @@ include: - .use-pg12-opensearch1-ee - .rails:rules:run-search-tests +.rspec-ee-base-pg12-opensearch2: + extends: + - .rspec-base + - .use-pg12-opensearch2-ee + - .rails:rules:run-search-tests + .rspec-ee-base-pg13: extends: - .rspec-base - - .use-pg13-ee + - .use-pg13-es7-ee .db-job-base: extends: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index d1ba727d232..f3f40256538 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -573,6 +573,8 @@ rules: - <<: *if-merge-request-labels-group-global-search changes: *search-backend-patterns + - <<: *if-merge-request-labels-group-global-search + changes: *ci-patterns .rails:rules:ee-and-foss-default-rules: rules: diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue index 9a734a12a46..96c2ffa929c 100644 --- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue +++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue @@ -145,7 +145,8 @@ export default { }, currentTabIndex: { get() { - return this.$options.tabsConfig.findIndex((tab) => tab.id === this.activeTab); + const tabIndex = this.$options.tabsConfig.findIndex((tab) => tab.id === this.activeTab); + return tabIndex >= 0 ? tabIndex : 0; }, set(tabIdx) { const tabId = this.$options.tabsConfig[tabIdx].id; diff --git a/app/assets/javascripts/vue_shared/alert_details/router.js b/app/assets/javascripts/vue_shared/alert_details/router.js index 616d5c259b9..26477a3a66a 100644 --- a/app/assets/javascripts/vue_shared/alert_details/router.js +++ b/app/assets/javascripts/vue_shared/alert_details/router.js @@ -5,9 +5,26 @@ import { joinPaths } from '~/lib/utils/url_utility'; Vue.use(VueRouter); export default function createRouter(base) { - return new VueRouter({ + const router = new VueRouter({ mode: 'history', base: joinPaths(gon.relative_url_root || '', base), routes: [{ path: '/:tabId', name: 'tab' }], }); + + /* + Backward-compatible behavior. Redirects hash mode URLs to history mode ones. + Ex: from #/overview to /overview + from #/metrics to /metrics + from #/activity to /activity + */ + router.beforeEach((to, _, next) => { + if (to.hash.startsWith('#/')) { + const path = to.fullPath.substring(2); + next(path); + } else { + next(); + } + }); + + return router; } diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index bc76408a5c2..aae0d9e52be 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -38,128 +38,6 @@ This content has been converted to a Rake task, see [verify database values can This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console). -## Users - -### Create new user - -```ruby -u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password') -u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail -u.save! -``` - -### Skip reconfirmation - -```ruby -user = User.find_by_username('') -user.skip_reconfirmation! -``` - -### Disable 2fa for single user - -**In GitLab 13.5 and later:** - -Use the code under [Disable 2FA | For a single user](../../security/two_factor_authentication.md#for-a-single-user) so that the target user -is notified that 2FA has been disabled. - -**In GitLab 13.4 and earlier:** - -```ruby -user = User.find_by_username('') -user.disable_two_factor! -``` - -### Active users & Historical users - -```ruby -# Active users on the instance, now -User.active.count - -# Users taking a seat on the instance -User.billable.count - -# The historical max on the instance as of the past year -::HistoricalData.max_historical_user_count(from: 1.year.ago.beginning_of_day, to: Time.current.end_of_day) -``` - -Using cURL and jq (up to a max 100, see [Pagination](../../api/index.md#pagination)): - -```shell -curl --silent --header "Private-Token: ********************" \ - "https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]' -``` - -### Update Daily Billable & Historical users - -```ruby -# Forces recount of historical (max) users -::HistoricalDataWorker.new.perform - -# Forces recount of daily billable users -identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users] -::Analytics::UsageTrends::CounterJobWorker.new.perform(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now) -``` - -### Block or Delete Users that have no projects or groups - -```ruby -users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)') - -# How many users are removed? -users.count - -# If that count looks sane: - -# You can either block the users: -users.each { |user| user.blocked? ? nil : user.block! } - -# Or you can delete them: - # need 'current user' (your user) for auditing purposes -current_user = User.find_by(username: '') - -users.each do |user| - DeleteUserWorker.perform_async(current_user.id, user.id) -end -``` - -### Deactivate Users that have no recent activity - -```ruby -days_inactive = 90 -inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago) - -inactive_users.each do |user| - puts "user '#{user.username}': #{user.last_activity_on}" - user.deactivate! -end -``` - -### Block Users that have no recent activity - -```ruby -days_inactive = 90 -inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago) - -inactive_users.each do |user| - puts "user '#{user.username}': #{user.last_activity_on}" - user.block! -end -``` - -### Find a user's max permissions for project/group - -```ruby -user = User.find_by_username 'username' -project = Project.find_by_full_path 'group/project' -user.max_member_access_for_project project.id -``` - -```ruby -user = User.find_by_username 'username' -group = Group.find_by_full_path 'group' -user.max_member_access_for_group group.id -``` - ## Merge requests ## CI diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md index 3554c3e9111..56a36395afd 100644 --- a/doc/development/pipelines/index.md +++ b/doc/development/pipelines/index.md @@ -85,7 +85,7 @@ In this mode, `jest` would resolve all the dependencies of related to the change In addition, there are a few circumstances where we would always run the full Jest tests: -- when the `pipeline:run-all-jest` label is set on the merge request +- when the `pipeline:run-all-jest` label is set on the merge request or is set by triage automation when the merge request is approved by any reviewer - when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch) - when the merge request is created in a security mirror - when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`) diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md index cc06e824d14..e4d59b6b465 100644 --- a/doc/security/two_factor_authentication.md +++ b/doc/security/two_factor_authentication.md @@ -104,6 +104,8 @@ instead of the Rails console. Using the [Rails console](../administration/operations/rails_console.md), 2FA for a single user can be disabled. Connect to the Rails console and run: +**In GitLab 13.5 and later:** + ```ruby admin = User.find_by_username('') user_to_disable = User.find_by_username('') @@ -111,6 +113,9 @@ user_to_disable = User.find_by_username('') TwoFactor::DestroyService.new(admin, user: user_to_disable).execute ``` +NOTE: +The target user is notified that 2FA has been disabled. + ### For all users There may be some special situations where you want to disable 2FA for everyone diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md index 6cfc7aee6fe..6a718b31593 100644 --- a/doc/subscriptions/self_managed/index.md +++ b/doc/subscriptions/self_managed/index.md @@ -460,3 +460,40 @@ If your credit card is declined when purchasing a GitLab subscription, possible Check with your financial institution to confirm if any of these reasons apply. If they don't apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). + +### Check daily and historical billable users + +You can get a list of daily and historical billable users in your GitLab instance. + +1. [Start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session). + +1. Count the number of users in the instance: + + ```ruby + User.billable.count + ``` + +1. Get the historical maximum number of users on the instance from the past year: + + ```ruby + ::HistoricalData.max_historical_user_count(from: 1.year.ago.beginning_of_day, to: Time.current.end_of_day) + ``` + +### Update daily billable and historical users + +You can trigger a manual update of the daily and historical billable users in your GitLab instance. + +1. [Start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session). + +1. Force an update of the daily billable users: + + ```ruby + identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users] + ::Analytics::UsageTrends::CounterJobWorker.new.perform(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now) + ``` + +1. Force an update of the historical max billable users: + + ```ruby + ::HistoricalDataWorker.new.perform + ``` diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md index 6cee978095d..f1adfc115f2 100644 --- a/doc/user/admin_area/moderate_users.md +++ b/doc/user/admin_area/moderate_users.md @@ -283,3 +283,62 @@ You can also delete a user and their contributions, such as merge requests, issu NOTE: Before 15.1, additionally groups of which deleted user were the only owner among direct members were deleted. + +## Troubleshooting + +When moderating users, you may need to perform bulk actions on them based on certain conditions. The following rails console scripts show some examples of this. You may [start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session) and use scripts similar to the following: + +### Deactivate Users that have no recent activity + +WARNING: +Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore. + +```ruby +days_inactive = 90 +inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago) + +inactive_users.each do |user| + puts "user '#{user.username}': #{user.last_activity_on}" + user.deactivate! +end +``` + +### Block Users that have no recent activity + +WARNING: +Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore. + +```ruby +days_inactive = 90 +inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago) + +inactive_users.each do |user| + puts "user '#{user.username}': #{user.last_activity_on}" + user.block! +end +``` + +### Block or Delete Users that have no projects or groups + +WARNING: +Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore. + +```ruby +users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)') + +# How many users are removed? +users.count + +# If that count looks sane: + +# You can either block the users: +users.each { |user| user.blocked? ? nil : user.block! } + +# Or you can delete them: + # need 'current user' (your user) for auditing purposes +current_user = User.find_by(username: '') + +users.each do |user| + DeleteUserWorker.perform_async(current_user.id, user.id) +end +``` diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index 453182b8634..d05006ba944 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -859,3 +859,17 @@ Commands that change data can cause damage if not run correctly or under the rig ```ruby GroupDestroyWorker.new.perform(group_id, user_id) ``` + +### Find a user's max permissions for a group or project + +```ruby +user = User.find_by_username 'username' +project = Project.find_by_full_path 'group/project' +user.max_member_access_for_project project.id +``` + +```ruby +user = User.find_by_username 'username' +group = Group.find_by_full_path 'group' +user.max_member_access_for_group group.id +``` diff --git a/doc/user/profile/account/create_accounts.md b/doc/user/profile/account/create_accounts.md index f2636fdaa68..fdfb3b131d0 100644 --- a/doc/user/profile/account/create_accounts.md +++ b/doc/user/profile/account/create_accounts.md @@ -49,3 +49,16 @@ Users are: - Created when first signing with [Group SAML](../../group/saml_sso/index.md). - Automatically created by [SCIM](../../group/saml_sso/scim_setup.md) when the user is created in the identity provider. + +## Create users through the Rails console + +WARNING: +Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore. + +To create a user through the rails console, [start a console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session) and run the following commands: + +```ruby +u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password') +u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail +u.save! +``` diff --git a/lib/api/api.rb b/lib/api/api.rb index 8de5ed042d2..08269c81743 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -171,6 +171,7 @@ module API namespace do mount ::API::AccessRequests mount ::API::Appearance + mount ::API::BulkImports mount ::API::Ci::Runner mount ::API::Clusters::Agents mount ::API::Clusters::AgentTokens @@ -207,7 +208,6 @@ module API mount ::API::Boards mount ::API::Branches mount ::API::BroadcastMessages - mount ::API::BulkImports mount ::API::Ci::JobArtifacts mount ::API::Ci::Jobs mount ::API::Ci::PipelineSchedules diff --git a/lib/api/bulk_imports.rb b/lib/api/bulk_imports.rb index c54632919be..a28db321348 100644 --- a/lib/api/bulk_imports.rb +++ b/lib/api/bulk_imports.rb @@ -41,7 +41,15 @@ module API resource :bulk_imports do desc 'Start a new GitLab Migration' do detail 'This feature was introduced in GitLab 14.2.' - success Entities::BulkImport + success code: 200, model: Entities::BulkImport + consumes ['application/x-www-form-urlencoded'] + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 400, message: 'Bad request' }, + { code: 404, message: 'Not found' }, + { code: 422, message: 'Unprocessable entity' }, + { code: 503, message: 'Service unavailable' } + ] end params do requires :configuration, type: Hash, desc: 'The source GitLab instance configuration' do @@ -88,7 +96,13 @@ module API desc 'List all GitLab Migrations' do detail 'This feature was introduced in GitLab 14.1.' - success Entities::BulkImport + is_array true + success code: 200, model: Entities::BulkImport + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 503, message: 'Service unavailable' } + ] end params do use :pagination @@ -103,7 +117,13 @@ module API desc "List all GitLab Migrations' entities" do detail 'This feature was introduced in GitLab 14.1.' - success Entities::BulkImports::Entity + is_array true + success code: 200, model: Entities::BulkImports::Entity + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 503, message: 'Service unavailable' } + ] end params do use :pagination @@ -123,7 +143,12 @@ module API desc 'Get GitLab Migration details' do detail 'This feature was introduced in GitLab 14.1.' - success Entities::BulkImport + success code: 200, model: Entities::BulkImport + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 503, message: 'Service unavailable' } + ] end params do requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration" @@ -134,7 +159,13 @@ module API desc "List GitLab Migration entities" do detail 'This feature was introduced in GitLab 14.1.' - success Entities::BulkImports::Entity + is_array true + success code: 200, model: Entities::BulkImports::Entity + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 503, message: 'Service unavailable' } + ] end params do requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration" @@ -148,7 +179,12 @@ module API desc 'Get GitLab Migration entity details' do detail 'This feature was introduced in GitLab 14.1.' - success Entities::BulkImports::Entity + success code: 200, model: Entities::BulkImports::Entity + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 503, message: 'Service unavailable' } + ] end params do requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration" diff --git a/lib/api/entities/bulk_import.rb b/lib/api/entities/bulk_import.rb index 373ae486dcf..75989cb4180 100644 --- a/lib/api/entities/bulk_import.rb +++ b/lib/api/entities/bulk_import.rb @@ -3,11 +3,13 @@ module API module Entities class BulkImport < Grape::Entity - expose :id - expose :status_name, as: :status - expose :source_type - expose :created_at - expose :updated_at + expose :id, documentation: { type: 'integer', example: 1 } + expose :status_name, as: :status, documentation: { + type: 'string', example: 'finished', values: %w[created started finished timeout failed] + } + expose :source_type, documentation: { type: 'string', example: 'gitlab' } + expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' } + expose :updated_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' } end end end diff --git a/lib/api/entities/bulk_imports/entity.rb b/lib/api/entities/bulk_imports/entity.rb index 142bfaf2149..8f9fbe57935 100644 --- a/lib/api/entities/bulk_imports/entity.rb +++ b/lib/api/entities/bulk_imports/entity.rb @@ -4,19 +4,21 @@ module API module Entities module BulkImports class Entity < Grape::Entity - expose :id - expose :bulk_import_id - expose :status_name, as: :status - expose :source_full_path - expose :destination_name # deprecated - expose :destination_slug - expose :destination_namespace - expose :parent_id - expose :namespace_id - expose :project_id - expose :created_at - expose :updated_at - expose :failures, using: EntityFailure + expose :id, documentation: { type: 'integer', example: 1 } + expose :bulk_import_id, documentation: { type: 'integer', example: 1 } + expose :status_name, as: :status, documentation: { + type: 'string', example: 'created', values: %w[created started finished timeout failed] + } + expose :source_full_path, documentation: { type: 'string', example: 'source_group' } + expose :destination_name, documentation: { type: 'string', example: 'destination_slug' } # deprecated + expose :destination_slug, documentation: { type: 'string', example: 'destination_slug' } + expose :destination_namespace, documentation: { type: 'string', example: 'destination_path' } + expose :parent_id, documentation: { type: 'integer', example: 1 } + expose :namespace_id, documentation: { type: 'integer', example: 1 } + expose :project_id, documentation: { type: 'integer', example: 1 } + expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' } + expose :updated_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' } + expose :failures, using: EntityFailure, documentation: { is_array: true } end end end diff --git a/lib/api/entities/bulk_imports/entity_failure.rb b/lib/api/entities/bulk_imports/entity_failure.rb index 56312745868..3e69e7fa2aa 100644 --- a/lib/api/entities/bulk_imports/entity_failure.rb +++ b/lib/api/entities/bulk_imports/entity_failure.rb @@ -4,16 +4,18 @@ module API module Entities module BulkImports class EntityFailure < Grape::Entity - expose :relation - expose :pipeline_step, as: :step - expose :exception_message do |failure| + expose :relation, documentation: { type: 'string', example: 'group' } + expose :pipeline_step, as: :step, documentation: { type: 'string', example: 'extractor' } + expose :exception_message, documentation: { type: 'string', example: 'error message' } do |failure| ::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72)) end - expose :exception_class - expose :correlation_id_value - expose :created_at - expose :pipeline_class - expose :pipeline_step + expose :exception_class, documentation: { type: 'string', example: 'Exception' } + expose :correlation_id_value, documentation: { type: 'string', example: 'dfcf583058ed4508e4c7c617bd7f0edd' } + expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' } + expose :pipeline_class, documentation: { + type: 'string', example: 'BulkImports::Groups::Pipelines::GroupPipeline' + } + expose :pipeline_step, documentation: { type: 'string', example: 'extractor' } end end end diff --git a/spec/frontend/vue_shared/alert_details/router_spec.js b/spec/frontend/vue_shared/alert_details/router_spec.js new file mode 100644 index 00000000000..e3efc104862 --- /dev/null +++ b/spec/frontend/vue_shared/alert_details/router_spec.js @@ -0,0 +1,35 @@ +import createRouter from '~/vue_shared/alert_details/router'; +import setWindowLocation from 'helpers/set_window_location_helper'; + +const BASE_PATH = '/-/alert_management/1/details'; +const EMPTY_HASH = ''; +const NOOP = () => {}; + +describe('AlertDetails router', () => { + const originalLocation = window.location.href; + let router; + + beforeEach(() => { + setWindowLocation(originalLocation); + router = createRouter(BASE_PATH); + }); + + describe('redirects hash route mode URLs to history route mode', () => { + it.each` + hashPath | historyPath + ${'/#/overview'} | ${'/overview'} + ${'#/overview'} | ${'/overview'} + ${'/#/'} | ${'/'} + ${'#/'} | ${'/'} + ${'/#'} | ${'/'} + ${'#'} | ${'/'} + ${'/'} | ${'/'} + ${'/overview'} | ${'/overview'} + `('should redirect "$hashPath" to "$historyPath"', ({ hashPath, historyPath }) => { + router.push(hashPath, NOOP); + + expect(window.location.hash).toBe(EMPTY_HASH); + expect(window.location.pathname).toBe(BASE_PATH + historyPath); + }); + }); +});