Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-08-10 09:07:17 +00:00
parent 76c4dd062c
commit f605b80ff7
55 changed files with 760 additions and 332 deletions

View File

@ -59,8 +59,8 @@ Gitlab/StrongMemoizeAttr:
- 'app/graphql/resolvers/namespace_projects_resolver.rb'
- 'app/graphql/resolvers/work_items_resolver.rb'
- 'app/graphql/types/board_list_type.rb'
- 'app/helpers/admin/broadcast_messages_helper.rb'
- 'app/helpers/appearances_helper.rb'
- 'app/helpers/broadcast_messages_helper.rb'
- 'app/helpers/diff_helper.rb'
- 'app/helpers/operations_helper.rb'
- 'app/helpers/page_layout_helper.rb'

View File

@ -1277,12 +1277,12 @@ Layout/ArgumentAlignment:
- 'ee/spec/services/vulnerability_feedback/create_service_spec.rb'
- 'ee/spec/services/vulnerability_merge_request_links/create_service_spec.rb'
- 'lib/api/access_requests.rb'
- 'lib/api/admin/broadcast_messages.rb'
- 'lib/api/admin/plan_limits.rb'
- 'lib/api/alert_management_alerts.rb'
- 'lib/api/api.rb'
- 'lib/api/applications.rb'
- 'lib/api/branches.rb'
- 'lib/api/broadcast_messages.rb'
- 'lib/api/bulk_imports.rb'
- 'lib/api/ci/job_artifacts.rb'
- 'lib/api/ci/jobs.rb'

View File

@ -2409,13 +2409,13 @@ Layout/LineLength:
- 'ee/spec/workers/sync_seat_link_request_worker_spec.rb'
- 'ee/spec/workers/update_all_mirrors_worker_spec.rb'
- 'ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
- 'lib/api/admin/broadcast_messages.rb'
- 'lib/api/admin/instance_clusters.rb'
- 'lib/api/api.rb'
- 'lib/api/appearance.rb'
- 'lib/api/award_emoji.rb'
- 'lib/api/boards_responses.rb'
- 'lib/api/branches.rb'
- 'lib/api/broadcast_messages.rb'
- 'lib/api/ci/jobs.rb'
- 'lib/api/ci/pipeline_schedules.rb'
- 'lib/api/ci/pipelines.rb'
@ -2435,10 +2435,10 @@ Layout/LineLength:
- 'lib/api/deploy_tokens.rb'
- 'lib/api/deployments.rb'
- 'lib/api/discussions.rb'
- 'lib/api/entities/system/broadcast_message.rb'
- 'lib/api/entities/application_setting.rb'
- 'lib/api/entities/basic_project_details.rb'
- 'lib/api/entities/branch.rb'
- 'lib/api/entities/broadcast_message.rb'
- 'lib/api/entities/container_registry.rb'
- 'lib/api/entities/deploy_key.rb'
- 'lib/api/entities/issue_basic.rb'
@ -4336,6 +4336,7 @@ Layout/LineLength:
- 'spec/rack_servers/puma_spec.rb'
- 'spec/requests/admin/background_migrations_controller_spec.rb'
- 'spec/requests/api/access_requests_spec.rb'
- 'spec/requests/api/admin/broadcast_messages_spec.rb'
- 'spec/requests/api/admin/instance_clusters_spec.rb'
- 'spec/requests/api/admin/plan_limits_spec.rb'
- 'spec/requests/api/admin/sidekiq_spec.rb'
@ -4344,7 +4345,6 @@ Layout/LineLength:
- 'spec/requests/api/award_emoji_spec.rb'
- 'spec/requests/api/badges_spec.rb'
- 'spec/requests/api/branches_spec.rb'
- 'spec/requests/api/broadcast_messages_spec.rb'
- 'spec/requests/api/bulk_imports_spec.rb'
- 'spec/requests/api/ci/job_artifacts_spec.rb'
- 'spec/requests/api/ci/jobs_spec.rb'

View File

@ -1,6 +1,7 @@
---
Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/admin/broadcast_messages_helper.rb'
- 'app/helpers/admin/user_actions_helper.rb'
- 'app/helpers/application_helper.rb'
- 'app/helpers/application_settings_helper.rb'
@ -9,7 +10,6 @@ Rails/HelperInstanceVariable:
- 'app/helpers/boards_helper.rb'
- 'app/helpers/branches_helper.rb'
- 'app/helpers/breadcrumbs_helper.rb'
- 'app/helpers/broadcast_messages_helper.rb'
- 'app/helpers/ci/builds_helper.rb'
- 'app/helpers/ci/jobs_helper.rb'
- 'app/helpers/commits_helper.rb'

View File

@ -13,10 +13,10 @@ Rails/OutputSafety:
- 'app/controllers/search_controller.rb'
- 'app/graphql/types/ci/job_trace_type.rb'
- 'app/graphql/types/project_type.rb'
- 'app/helpers/admin/broadcast_messages_helper.rb'
- 'app/helpers/admin/application_settings/settings_helper.rb'
- 'app/helpers/appearances_helper.rb'
- 'app/helpers/auth_helper.rb'
- 'app/helpers/broadcast_messages_helper.rb'
- 'app/helpers/commits_helper.rb'
- 'app/helpers/diff_helper.rb'
- 'app/helpers/dropdowns_helper.rb'

View File

@ -1096,8 +1096,8 @@ RSpec/BeforeAllRoleAssignment:
- 'spec/graphql/types/todo_type_spec.rb'
- 'spec/graphql/types/user_merge_request_interaction_type_spec.rb'
- 'spec/graphql/types/user_type_spec.rb'
- 'spec/helpers/admin/broadcast_messages_helper_spec.rb'
- 'spec/helpers/admin/user_actions_helper_spec.rb'
- 'spec/helpers/broadcast_messages_helper_spec.rb'
- 'spec/helpers/ci/pipelines_helper_spec.rb'
- 'spec/helpers/clusters_helper_spec.rb'
- 'spec/helpers/groups_helper_spec.rb'

View File

@ -250,6 +250,7 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/components/diffs/overflow_warning_component_spec.rb'
- 'spec/components/diffs/stats_component_spec.rb'
- 'spec/components/pajamas/avatar_component_spec.rb'
- 'spec/helpers/admin/broadcast_messages_helper_spec.rb'
- 'spec/helpers/admin/identities_helper_spec.rb'
- 'spec/helpers/admin/user_actions_helper_spec.rb'
- 'spec/helpers/appearances_helper_spec.rb'
@ -262,7 +263,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/helpers/blob_helper_spec.rb'
- 'spec/helpers/boards_helper_spec.rb'
- 'spec/helpers/branches_helper_spec.rb'
- 'spec/helpers/broadcast_messages_helper_spec.rb'
- 'spec/helpers/button_helper_spec.rb'
- 'spec/helpers/calendar_helper_spec.rb'
- 'spec/helpers/ci/builds_helper_spec.rb'

View File

@ -930,8 +930,8 @@ Style/PercentLiteralDelimiters:
- 'spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb'
- 'spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb'
- 'spec/presenters/packages/nuget/search_results_presenter_spec.rb'
- 'spec/requests/api/admin/broadcast_messages_spec.rb'
- 'spec/requests/api/badges_spec.rb'
- 'spec/requests/api/broadcast_messages_spec.rb'
- 'spec/requests/api/ci/jobs_spec.rb'
- 'spec/requests/api/ci/pipelines_spec.rb'
- 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'

View File

@ -52,6 +52,8 @@ export default {
RunnerTypeTabs,
RunnerActionsCell,
RunnerJobStatusBadge,
RunnerDashboardLink: () =>
import('ee_component/ci/runner/components/runner_dashboard_link.vue'),
},
mixins: [glFeatureFlagMixin()],
props: {
@ -188,12 +190,12 @@ export default {
nav-class="gl-border-none!"
/>
<div class="gl-w-full gl-md-w-auto gl-display-flex">
<div class="gl-w-full gl-md-w-auto gl-display-flex gl-gap-3">
<runner-dashboard-link />
<gl-button :href="newRunnerPath" variant="confirm">
{{ s__('Runners|New instance runner') }}
</gl-button>
<registration-dropdown
class="gl-ml-3"
:registration-token="registrationToken"
:type="$options.INSTANCE_TYPE"
placement="right"

View File

@ -1,6 +1,9 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { provide } from 'ee_else_ce/ci/runner/admin_runners/provide';
import { visitUrl } from '~/lib/utils/url_utility';
import { updateOutdatedUrl } from '~/ci/runner/runner_search_utils';
import createDefaultClient from '~/lib/graphql';
@ -29,14 +32,7 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
return null;
}
const {
runnerInstallHelpPage,
newRunnerPath,
registrationToken,
onlineContactTimeoutSecs,
staleTimeoutSecs,
} = el.dataset;
const { newRunnerPath, registrationToken } = el.dataset;
const { cacheConfig, typeDefs, localMutations } = createLocalState();
const apolloProvider = new VueApollo({
@ -47,10 +43,8 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
el,
apolloProvider,
provide: {
runnerInstallHelpPage,
...provide(el.dataset),
localMutations,
onlineContactTimeoutSecs,
staleTimeoutSecs,
},
render(h) {
return h(AdminRunnersApp, {

View File

@ -0,0 +1,16 @@
/**
* Provides global values to the admin runners app.
*
* @param {Object} `data-` HTML attributes of the mounting point
* @returns An object with properties to use provide/inject of the root app.
* See EE version
*/
export const provide = (elDataset) => {
const { runnerInstallHelpPage, onlineContactTimeoutSecs, staleTimeoutSecs } = elDataset;
return {
runnerInstallHelpPage,
onlineContactTimeoutSecs,
staleTimeoutSecs,
};
};

View File

@ -15,21 +15,21 @@ import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { joinPaths } from '~/lib/utils/url_utility';
import { getBulkImportsHistory } from '~/rest_api';
import ImportStatus from '~/import_entities/components/import_status.vue';
import { StatusPoller } from '~/import_entities/import_groups/services/status_poller';
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { isImporting } from '../utils';
import { DEFAULT_ERROR } from '../utils/error_messages';
const DEFAULT_PER_PAGE = 20;
const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-200! gl-border-b-1! gl-p-5!';
const HISTORY_PAGINATION_SIZE_PERSIST_KEY = 'gl-bulk-imports-history-per-page';
const tableCell = (config) => ({
thClass: `${DEFAULT_TH_CLASSES}`,
tdClass: (value, key, item) => {
return {
// eslint-disable-next-line no-underscore-dangle
@ -57,6 +57,8 @@ export default {
GlTooltip,
},
inject: ['realtimeChangesPath'],
data() {
return {
loading: true,
@ -73,12 +75,12 @@ export default {
tableCell({
key: 'source_full_path',
label: s__('BulkImport|Source'),
thClass: `${DEFAULT_TH_CLASSES} gl-w-30p`,
thClass: `gl-w-30p`,
}),
tableCell({
key: 'destination_name',
label: s__('BulkImport|Destination'),
thClass: `${DEFAULT_TH_CLASSES} gl-w-40p`,
thClass: `gl-w-40p`,
}),
tableCell({
key: 'created_at',
@ -95,6 +97,12 @@ export default {
hasHistoryItems() {
return this.historyItems.length > 0;
},
importingHistoryItemIds() {
return this.historyItems
.filter((item) => isImporting(item.status))
.map((item) => item.bulk_import_id);
},
},
watch: {
@ -104,10 +112,43 @@ export default {
},
deep: true,
},
importingHistoryItemIds(value) {
if (value.length > 0) {
this.statusPoller.startPolling();
} else {
this.statusPoller.stopPolling();
}
},
},
mounted() {
this.loadHistoryItems();
this.statusPoller = new StatusPoller({
pollPath: this.realtimeChangesPath,
updateImportStatus: (update) => {
if (!this.importingHistoryItemIds.includes(update.id)) {
return;
}
const updateItemIndex = this.historyItems.findIndex(
(item) => item.bulk_import_id === update.id,
);
const updateItem = this.historyItems[updateItemIndex];
if (updateItem.status !== update.status_name) {
this.$set(this.historyItems, updateItemIndex, {
...updateItem,
status: update.status_name,
});
}
},
});
},
beforeDestroy() {
this.statusPoller.stopPolling();
},
methods: {

View File

@ -4,8 +4,14 @@ import BulkImportHistoryApp from './components/bulk_imports_history_app.vue';
function mountImportHistoryApp(mountElement) {
if (!mountElement) return undefined;
const { realtimeChangesPath } = mountElement.dataset;
return new Vue({
el: mountElement,
name: 'BulkImportHistoryRoot',
provide: {
realtimeChangesPath,
},
render(createElement) {
return createElement(BulkImportHistoryApp);
},

View File

@ -0,0 +1,7 @@
import { STATUSES } from '~/import_entities/constants';
export function isImporting(status) {
return [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.CREATED, STATUSES.STARTED].includes(
status,
);
}

View File

@ -91,7 +91,7 @@
}
.md-preview-holder {
min-height: 173px;
min-height: 177px;
padding: 10px 0;
overflow-x: auto;
}

View File

@ -2,7 +2,7 @@
module Admin
class BroadcastMessagesController < ApplicationController
include BroadcastMessagesHelper
include Admin::BroadcastMessagesHelper
before_action :find_broadcast_message, only: [:edit, :update, :destroy]
before_action :find_broadcast_messages, only: [:index, :create]

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module GoogleSyndicationCSP
extend ActiveSupport::Concern
ALLOWED_SRC = ['*.google.com/pagead/landing', 'pagead2.googlesyndication.com/pagead/landing'].freeze
included do
content_security_policy do |policy|
next unless helpers.google_tag_manager_enabled? || policy.directives.present?
connect_src_values = Array.wrap(
policy.directives['connect-src'] || policy.directives['default-src']
)
connect_src_values.concat(ALLOWED_SRC) if helpers.google_tag_manager_enabled?
policy.connect_src(*connect_src_values.uniq)
end
end
end

View File

@ -5,6 +5,7 @@ class ConfirmationsController < Devise::ConfirmationsController
include GitlabRecaptcha
include OneTrustCSP
include GoogleAnalyticsCSP
include GoogleSyndicationCSP
skip_before_action :required_signup_info
prepend_before_action :check_recaptcha, only: :create

View File

@ -4,6 +4,7 @@ module Registrations
class WelcomeController < ApplicationController
include OneTrustCSP
include GoogleAnalyticsCSP
include GoogleSyndicationCSP
include ::Gitlab::Utils::StrongMemoize
layout 'minimal'

View File

@ -8,6 +8,7 @@ class RegistrationsController < Devise::RegistrationsController
include OneTrustCSP
include BizibleCSP
include GoogleAnalyticsCSP
include GoogleSyndicationCSP
include PreferredLanguageSwitcher
include Gitlab::Tracking::Helpers::WeakPasswordErrorEvent
include SkipsAlreadySignedInMessage

View File

@ -13,6 +13,7 @@ class SessionsController < Devise::SessionsController
include BizibleCSP
include VerifiesWithEmail
include GoogleAnalyticsCSP
include GoogleSyndicationCSP
include PreferredLanguageSwitcher
include SkipsAlreadySignedInMessage

View File

@ -0,0 +1,123 @@
# frozen_string_literal: true
module Admin
module BroadcastMessagesHelper
include Gitlab::Utils::StrongMemoize
def current_broadcast_banner_messages
System::BroadcastMessage.current_banner_messages(
current_path: request.path,
user_access_level: current_user_access_level_for_project_or_group
).select do |message|
cookies["hide_broadcast_message_#{message.id}"].blank?
end
end
def current_broadcast_notification_message
not_hidden_messages = System::BroadcastMessage.current_notification_messages(
current_path: request.path,
user_access_level: current_user_access_level_for_project_or_group
).select do |message|
cookies["hide_broadcast_message_#{message.id}"].blank?
end
not_hidden_messages.last
end
def broadcast_message(message, opts = {})
return unless message.present?
render "shared/broadcast_message", { message: message, **opts }
end
def broadcast_message_status(broadcast_message)
if broadcast_message.active?
'Active'
elsif broadcast_message.ended?
'Expired'
else
'Pending'
end
end
def render_broadcast_message(broadcast_message)
if broadcast_message.notification?
Banzai.render_field_and_post_process(broadcast_message, :message, {
current_user: current_user,
skip_project_check: true,
broadcast_message_placeholders: true
}).html_safe
else
Banzai.render_field(broadcast_message, :message).html_safe
end
end
def target_access_level_options
System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS.map do |access_level|
[Gitlab::Access.human_access(access_level), access_level]
end
end
def target_access_levels_display(access_levels)
access_levels.map do |access_level|
Gitlab::Access.human_access(access_level)
end.join(', ')
end
def admin_broadcast_messages_data(broadcast_messages)
broadcast_messages.map do |message|
{
id: message.id,
status: broadcast_message_status(message),
message: message.message,
theme: message.theme,
broadcast_type: message.broadcast_type,
dismissable: message.dismissable,
starts_at: message.starts_at.iso8601,
ends_at: message.ends_at.iso8601,
target_roles: target_access_levels_display(message.target_access_levels),
target_path: message.target_path,
type: message.broadcast_type.capitalize,
edit_path: edit_admin_broadcast_message_path(message),
delete_path: "#{admin_broadcast_message_path(message)}.js"
}
end.to_json
end
def broadcast_message_data(broadcast_message)
{
id: broadcast_message.id,
message: broadcast_message.message,
broadcast_type: broadcast_message.broadcast_type,
theme: broadcast_message.theme,
dismissable: broadcast_message.dismissable.to_s,
target_access_levels: broadcast_message.target_access_levels,
messages_path: admin_broadcast_messages_path,
preview_path: preview_admin_broadcast_messages_path,
target_path: broadcast_message.target_path,
starts_at: broadcast_message.starts_at.iso8601,
ends_at: broadcast_message.ends_at.iso8601,
target_access_level_options: target_access_level_options.to_json,
show_in_cli: broadcast_message.show_in_cli.to_s
}
end
private
def current_user_access_level_for_project_or_group
return unless current_user.present?
strong_memoize(:current_user_access_level_for_project_or_group) do
case controller
when Projects::ApplicationController
next unless @project
@project.team.max_member_access(current_user.id)
when Groups::ApplicationController
next unless @group
@group.max_member_access_for_user(current_user)
end
end
end
end
end

View File

@ -1,121 +0,0 @@
# frozen_string_literal: true
module BroadcastMessagesHelper
include Gitlab::Utils::StrongMemoize
def current_broadcast_banner_messages
System::BroadcastMessage.current_banner_messages(
current_path: request.path,
user_access_level: current_user_access_level_for_project_or_group
).select do |message|
cookies["hide_broadcast_message_#{message.id}"].blank?
end
end
def current_broadcast_notification_message
not_hidden_messages = System::BroadcastMessage.current_notification_messages(
current_path: request.path,
user_access_level: current_user_access_level_for_project_or_group
).select do |message|
cookies["hide_broadcast_message_#{message.id}"].blank?
end
not_hidden_messages.last
end
def broadcast_message(message, opts = {})
return unless message.present?
render "shared/broadcast_message", { message: message, **opts }
end
def broadcast_message_status(broadcast_message)
if broadcast_message.active?
'Active'
elsif broadcast_message.ended?
'Expired'
else
'Pending'
end
end
def render_broadcast_message(broadcast_message)
if broadcast_message.notification?
Banzai.render_field_and_post_process(broadcast_message, :message, {
current_user: current_user,
skip_project_check: true,
broadcast_message_placeholders: true
}).html_safe
else
Banzai.render_field(broadcast_message, :message).html_safe
end
end
def target_access_level_options
System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS.map do |access_level|
[Gitlab::Access.human_access(access_level), access_level]
end
end
def target_access_levels_display(access_levels)
access_levels.map do |access_level|
Gitlab::Access.human_access(access_level)
end.join(', ')
end
def admin_broadcast_messages_data(broadcast_messages)
broadcast_messages.map do |message|
{
id: message.id,
status: broadcast_message_status(message),
message: message.message,
theme: message.theme,
broadcast_type: message.broadcast_type,
dismissable: message.dismissable,
starts_at: message.starts_at.iso8601,
ends_at: message.ends_at.iso8601,
target_roles: target_access_levels_display(message.target_access_levels),
target_path: message.target_path,
type: message.broadcast_type.capitalize,
edit_path: edit_admin_broadcast_message_path(message),
delete_path: "#{admin_broadcast_message_path(message)}.js"
}
end.to_json
end
def broadcast_message_data(broadcast_message)
{
id: broadcast_message.id,
message: broadcast_message.message,
broadcast_type: broadcast_message.broadcast_type,
theme: broadcast_message.theme,
dismissable: broadcast_message.dismissable.to_s,
target_access_levels: broadcast_message.target_access_levels,
messages_path: admin_broadcast_messages_path,
preview_path: preview_admin_broadcast_messages_path,
target_path: broadcast_message.target_path,
starts_at: broadcast_message.starts_at.iso8601,
ends_at: broadcast_message.ends_at.iso8601,
target_access_level_options: target_access_level_options.to_json,
show_in_cli: broadcast_message.show_in_cli.to_s
}
end
private
def current_user_access_level_for_project_or_group
return unless current_user.present?
strong_memoize(:current_user_access_level_for_project_or_group) do
case controller
when Projects::ApplicationController
next unless @project
@project.team.max_member_access(current_user.id)
when Groups::ApplicationController
next unless @group
@group.max_member_access_for_user(current_user)
end
end
end
end

View File

@ -3,4 +3,4 @@
- add_page_specific_style 'page_bundles/import'
- page_title _('Import history')
#import-history-mount-element
#import-history-mount-element{ data: { realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }

View File

@ -5,10 +5,15 @@ module Gitlab
# Module that provides methods shared by the various workers used for
# importing GitHub projects.
module ReschedulingMethods
extend ActiveSupport::Concern
include JobDelayCalculator
ENQUEUED_JOB_COUNT = 'github-importer/enqueued_job_count/%{project}/%{collection}'
included do
loggable_arguments 2
end
# project_id - The ID of the GitLab project to import the note into.
# hash - A Hash containing the details of the GitHub object to import.
# notify_key - The Redis key to notify upon completion, if any.

View File

@ -9,12 +9,7 @@ class EnsureBackfillForCiPipelineVariablesPipelineIdIsFinished < Gitlab::Databas
TABLE_NAME = :ci_pipeline_variables
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: TABLE_NAME,
column_name: 'pipeline_id',
job_arguments: [['pipeline_id'], ['id_convert_to_bigint']]
)
# no-op
end
def down

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class EnsureAgainBackfillForCiPipelineVariablesPipelineIdIsFinished < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
restrict_gitlab_migration gitlab_schema: :gitlab_ci
disable_ddl_transaction!
TABLE_NAME = :ci_pipeline_variables
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: TABLE_NAME,
column_name: 'id',
job_arguments: [['pipeline_id'], ['pipeline_id_convert_to_bigint']]
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class DropPreparedAtIndex < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'index_merge_requests_on_id_and_prepared_at'
# TODO: Issue for sync deletion: https://gitlab.com/gitlab-org/gitlab/-/issues/419917
def up
prepare_async_index_removal :merge_requests, :id, name: INDEX_NAME
end
def down
unprepare_async_index :merge_requests, :id, name: INDEX_NAME
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddPreparedAtCreatedAtIndex < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
INDEX_NAME = 'index_merge_requests_id_created_at_prepared_at'
# TODO: Issue for sync deletion: https://gitlab.com/gitlab-org/gitlab/-/issues/419918
def up
prepare_async_index(:merge_requests,
[:created_at, :id],
name: INDEX_NAME,
where: "prepared_at IS NULL")
end
def down
unprepare_async_index(:merge_requests, [:created_at, :id], name: INDEX_NAME)
end
end

View File

@ -0,0 +1 @@
e171f06f9b1a0824cd11216255b1f99976bdd97069a35a8514934d33e5b3e634

View File

@ -0,0 +1 @@
f71b5244de5b3ed97e38414b304df80fd94ce0855a624efd178ffedfa36dfd31

View File

@ -0,0 +1 @@
09f9ee7ddf7153dbdb151f15a9de245d722a908b67b799da8a80f1bd9cb93c31

View File

@ -44,7 +44,7 @@ A good example is as follows:
```ruby
desc 'Get all broadcast messages' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
success Entities::System::BroadcastMessage
end
params do
optional :page, type: Integer, desc: 'Current page number'
@ -53,7 +53,7 @@ end
get do
messages = System::BroadcastMessage.all
present paginate(messages), with: Entities::BroadcastMessage
present paginate(messages), with: Entities::System::BroadcastMessage
end
```

View File

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Manage your code **(FREE)**
Store your source files in a repository, create merge requests, and compile code hosted on GitLab.
Store your source files in a repository and create merge requests. Write, debug, and compile code hosted on GitLab.
- [Repositories](../user/project/repository/index.md)
- [Merge requests](../user/project/merge_requests/index.md)

View File

@ -77,6 +77,12 @@ the following sections and tables provide an alternative.
## `scan_finding` rule type
> - The scan result policy field `vulnerability_attributes` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123052) in GitLab 16.2 [with a flag](../../../administration/feature_flags.md) named `enforce_vulnerability_attributes_rules`. Disabled by default.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/418784) in GitLab 16.3.
FLAG:
On self-managed GitLab, by default the `vulnerability_attributes` field is available. To hide the feature, an administrator can [disable the feature flag](../../../administration/feature_flags.md) named `enforce_vulnerability_attributes_rules`.
On GitLab.com, this feature is available.
This rule enforces the defined actions based on security scan findings.
| Field | Type | Required | Possible values | Description |
@ -88,6 +94,7 @@ This rule enforces the defined actions based on security scan findings.
| `vulnerabilities_allowed` | `integer` | true | Greater than or equal to zero | Number of vulnerabilities allowed before this rule is considered. |
| `severity_levels` | `array` of `string` | true | `info`, `unknown`, `low`, `medium`, `high`, `critical` | The severity levels for this rule to consider. |
| `vulnerability_states` | `array` of `string` | true | `newly_detected`, `detected`, `confirmed`, `resolved`, `dismissed`, `new_needs_triage`, `new_dismissed` | All vulnerabilities fall into two categories:<br><br>**Newly Detected Vulnerabilities** - the `newly_detected` policy option covers vulnerabilities identified in the merge request branch itself but that do not currently exist on the default branch. This policy option requires a pipeline to complete before the rule is evaluated so that it knows whether vulnerabilities are newly detected or not. Merge requests are blocked until the pipeline and necessary security scans are complete. The `newly_detected` option considers both of the following statuses:<br><br> • Detected<br> • Dismissed<br><br> The `new_needs_triage` option considers the status<br><br> • Detected<br><br> The `new_dismissed` option considers the status<br><br> • Dismissed<br><br>**Pre-Existing Vulnerabilities** - these policy options are evaluated immediately and do not require a pipeline complete as they consider only vulnerabilities previously detected in the default branch.<br><br>`Detected` - the policy looks for vulnerabilities in the detected state.<br>`Confirmed` - the policy looks for vulnerabilities in the confirmed state.<br>`Dismissed` - the policy looks for vulnerabilities in the dismissed state.<br>`Resolved` - the policy looks for vulnerabilities in the resolved state. |
| `vulnerability_attributes` | `object` | false | `{false_positive: boolean, fix_available: boolean}` | All vulnerability findings are considered by default. But filters can be applied for attributes to consider only vulnerability findings: <br><br> • With a fix available (`fix_available: true`)<br><br> • With no fix available (`fix_available: false`)<br> • That are false positive (`false_positive: true`)<br> • That are not false positive (`false_positive: false`)<br> • Or a combination of both. For example (`fix_available: true, false_positive: false`) |
## `license_finding` rule type
@ -150,6 +157,9 @@ scan_result_policy:
- critical
vulnerability_states:
- newly_detected
vulnerability_attributes:
false_positive: true
fix_available: true
actions:
- type: require_approval
approvals_required: 1

View File

@ -40,41 +40,43 @@ To check if a user's SAML `NameId` matches their SCIM `externalId`:
- Administrators can use the Admin Area to [list SCIM identities for a user](../../../administration/admin_area.md#user-identities).
- Group owners can see the list of users and the identifier stored for each user in the group SAML SSO Settings page.
- You can use the [SCIM API](../../../api/scim.md) to manually retrieve the `external_uid` GitLab has stored for users and compare the value for each user from the [SAML API](../../../api/saml.md) .
- Have the user use a [SAML Tracer](troubleshooting.md#saml-debugging-tools) and compare the `external_uid` to
- You can use the [SCIM API](../../../api/scim.md) to manually retrieve the `extern_uid` GitLab has stored for users and compare the value for each user from the [SAML API](../../../api/saml.md) .
- Have the user use a [SAML Tracer](troubleshooting.md#saml-debugging-tools) and compare the `extern_uid` to
the value returned as the SAML `NameId`.
## Mismatched SCIM `extern_uid` and SAML `NameId`
Whether the value was changed or you need to map to a different field, the following must map to the same field:
- `id`
- `externalId`
- `NameId`
If the GitLab `extern_uid` doesn't match the SAML `NameId`, it must be updated for the user to sign in. Your identity
provider should be configured to do this update. In some cases the identity provider cannot do the update, for example
when a user lookup fails because of an ID change.
If the GitLab `extern_uid` does not match the SAML `NameId`, you must update the GitLab `extern_uid` to enable the user to sign in.
Be cautious if you revise the fields used by your SCIM identity provider, typically `id` and `externalId`.
GitLab uses these IDs to look up users. If the identity provider does not know the current values for these fields,
that provider may create duplicate users.
Be cautious if you revise the fields used by your SCIM identity provider, typically `externalId`.
Your identity provider should be configured to do this update.
In some cases the identity provider cannot do the update, for example when a user lookup fails.
If the `extern_uid` for a user is not correct, and also doesn't match the SAML `NameID`, either:
GitLab uses these IDs to look up users.
If the identity provider does not know the current values for these fields,
that provider may create duplicate users, or fail to complete expected actions.
- Have users unlink and relink themselves, based on the
To change the identifier values to match:
1. Have users unlink and relink themselves, based on the
[SAML authentication failed: User has already been taken](troubleshooting.md#message-saml-authentication-failed-user-has-already-been-taken)
section.
- Unlink all users simultaneously by removing all users from the SCIM app while provisioning is turned on.
- Use the [SCIM API](../../../api/scim.md) to manually correct the `extern_uid` stored for users to match the SAML
`NameId`. To look up a user, you must know the desired value that matches the `NameId` as well as the current
`extern_uid`.
1. Unlink all users simultaneously by removing all users from the SCIM app while provisioning is turned on.
1. Use the [SAML API](../../../api/saml.md) or [SCIM API](../../../api/scim.md) to manually correct the `extern_uid` stored for users to match the SAML
`NameId` or SCIM `externalId`.
You must not:
- Update these to incorrect values because this causes users to be unable to sign in.
- Assign a value to the wrong user because this causes users to be signed in to the wrong account.
Additionally, the user's primary email must match the email in your SCIM identity provider.
## Change SCIM app
When the SCIM app changes:

View File

@ -72,6 +72,7 @@ GitLab creates a squash commit message with this template:
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/26303) `all_commits` variable in GitLab 14.9.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/378352) `reviewed_by` variable in GitLab 15.7.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/199823) `local_reference` variable in GitLab 16.1.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128553) `source_project_id` variables in GitLab 16.3.
Commit message templates support these variables:
@ -84,6 +85,7 @@ Commit message templates support these variables:
| `%{description}` | Description of the merge request. | `Merge request description.`<br>`Can be multiline.` |
| `%{reference}` | Reference to the merge request. | `group-name/project-name!72359` |
| `%{local_reference}` | Local reference to the merge request. | `!72359` |
| `%{source_project_id}` | ID of the merge request's source project. | `123` |
| `%{first_commit}` | Full message of the first commit in merge request diff. | `Update README.md` |
| `%{first_multiline_commit}` | Full message of the first commit that's not a merge commit and has more than one line in message body. Merge request title if all commits aren't multiline. | `Update README.md`<br><br>`Improved project description in readme file.` |
| `%{url}` | Full URL to the merge request. | `https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1` |

View File

@ -34,7 +34,7 @@ With remote development, you can use:
For a complete IDE experience, connect the Web IDE to a development environment configured to run as a remote host. You can create this environment [inside](../../workspace/index.md) or [outside](connect_machine.md) of GitLab.
## Workspace
## Workspaces **(PREMIUM)**
A [workspace](../../workspace/index.md) is a virtual sandbox environment for your code in GitLab that includes:

View File

@ -0,0 +1,127 @@
# frozen_string_literal: true
module API
module Admin
class BroadcastMessages < ::API::Base
include PaginationParams
feature_category :onboarding
urgency :low
resource :broadcast_messages do
helpers do
def find_message
System::BroadcastMessage.find(params[:id])
end
end
desc 'Get all broadcast messages' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::System::BroadcastMessage
end
params do
use :pagination
end
get do
messages = System::BroadcastMessage.all.order_id_desc
present paginate(messages), with: Entities::System::BroadcastMessage
end
desc 'Create a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::System::BroadcastMessage
end
params do
requires :message, type: String, desc: 'Message to display'
optional :starts_at, type: DateTime, desc: 'Starting time', default: -> { Time.zone.now }
optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now }
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
optional :target_access_levels,
type: Array[Integer],
coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
values: System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: System::BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> {
'banner'
}
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end
post do
authenticated_as_admin!
message = System::BroadcastMessage.create(declared_params(include_missing: false))
if message.persisted?
present message, with: Entities::System::BroadcastMessage
else
render_validation_error!(message)
end
end
desc 'Get a specific broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::System::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
end
get ':id' do
message = find_message
present message, with: Entities::System::BroadcastMessage
end
desc 'Update a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::System::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
optional :message, type: String, desc: 'Message to display'
optional :starts_at, type: DateTime, desc: 'Starting time'
optional :ends_at, type: DateTime, desc: 'Ending time'
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
optional :target_access_levels,
type: Array[Integer],
coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
values: System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: System::BroadcastMessage.broadcast_types.keys,
desc: 'Broadcast Type'
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end
put ':id' do
authenticated_as_admin!
message = find_message
if message.update(declared_params(include_missing: false))
present message, with: Entities::System::BroadcastMessage
else
render_validation_error!(message)
end
end
desc 'Delete a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::System::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
end
delete ':id' do
authenticated_as_admin!
message = find_message
destroy_conditionally!(message)
end
end
end
end
end

View File

@ -188,6 +188,7 @@ module API
# Keep in alphabetical order
mount ::API::AccessRequests
mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::BroadcastMessages
mount ::API::Admin::Ci::Variables
mount ::API::Admin::Dictionary
mount ::API::Admin::InstanceClusters
@ -199,7 +200,6 @@ module API
mount ::API::Avatar
mount ::API::Badges
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::BulkImports
mount ::API::Ci::JobArtifacts
mount ::API::Groups

View File

@ -1,122 +0,0 @@
# frozen_string_literal: true
module API
class BroadcastMessages < ::API::Base
include PaginationParams
feature_category :onboarding
urgency :low
resource :broadcast_messages do
helpers do
def find_message
System::BroadcastMessage.find(params[:id])
end
end
desc 'Get all broadcast messages' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
use :pagination
end
get do
messages = System::BroadcastMessage.all.order_id_desc
present paginate(messages), with: Entities::BroadcastMessage
end
desc 'Create a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
requires :message, type: String, desc: 'Message to display'
optional :starts_at, type: DateTime, desc: 'Starting time', default: -> { Time.zone.now }
optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now }
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
optional :target_access_levels,
type: Array[Integer],
coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
values: System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: System::BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' }
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end
post do
authenticated_as_admin!
message = System::BroadcastMessage.create(declared_params(include_missing: false))
if message.persisted?
present message, with: Entities::BroadcastMessage
else
render_validation_error!(message)
end
end
desc 'Get a specific broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
end
get ':id' do
message = find_message
present message, with: Entities::BroadcastMessage
end
desc 'Update a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
optional :message, type: String, desc: 'Message to display'
optional :starts_at, type: DateTime, desc: 'Starting time'
optional :ends_at, type: DateTime, desc: 'Ending time'
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
optional :target_access_levels,
type: Array[Integer],
coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
values: System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS,
desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: System::BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type'
optional :dismissable, type: Boolean, desc: 'Is dismissable'
end
put ':id' do
authenticated_as_admin!
message = find_message
if message.update(declared_params(include_missing: false))
present message, with: Entities::BroadcastMessage
else
render_validation_error!(message)
end
end
desc 'Delete a broadcast message' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
requires :id, type: Integer, desc: 'Broadcast message ID'
end
delete ':id' do
authenticated_as_admin!
message = find_message
destroy_conditionally!(message)
end
end
end
end

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
module API
module Entities
class BroadcastMessage < Grape::Entity
expose :id, :message, :starts_at, :ends_at, :color, :font, :target_access_levels, :target_path, :broadcast_type, :dismissable
expose :active?, as: :active
end
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module API
module Entities
module System
class BroadcastMessage < Grape::Entity
expose :id, :message, :starts_at, :ends_at, :color, :font, :target_access_levels, :target_path,
:broadcast_type, :dismissable
expose :active?, as: :active
end
end
end
end

View File

@ -52,6 +52,7 @@ module Gitlab
'description' => ->(merge_request, _, _) { merge_request.description },
'reference' => ->(merge_request, _, _) { merge_request.to_reference(full: true) },
'local_reference' => ->(merge_request, _, _) { merge_request.to_reference(full: false) },
'source_project_id' => ->(merge_request, _, _) { merge_request.source_project.id.to_s },
'first_commit' => -> (merge_request, _, _) {
return unless merge_request.persisted? || merge_request.compare_commits.present?

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Google Syndication content security policy', feature_category: :purchase do
include ContentSecurityPolicyHelpers
let_it_be(:connect_src) { 'https://other-cdn.test' }
let_it_be(:google_analytics_src) do
'localhost https://cdn.cookielaw.org https://*.onetrust.com *.google-analytics.com ' \
'*.analytics.google.com *.googletagmanager.com'
end
let_it_be(:allowed_src) do
'*.google.com/pagead/landing pagead2.googlesyndication.com/pagead/landing'
end
let(:extra) { { google_tag_manager_nonce_id: 'google_tag_manager_nonce_id' } }
let(:csp) do
ActionDispatch::ContentSecurityPolicy.new do |p|
p.connect_src(*connect_src.split)
end
end
subject { response_headers['Content-Security-Policy'] }
before do
setup_csp_for_controller(SessionsController, csp, any_time: true)
stub_config(extra: extra)
visit new_user_session_path
end
context 'when self-hosted' do
context 'when there is no CSP config' do
let(:extra) { {} }
let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
it { is_expected.to be_blank }
end
context 'when connect-src CSP config exists' do
it { is_expected.to include("connect-src #{connect_src} #{google_analytics_src}") }
it { is_expected.not_to include(allowed_src) }
end
end
context 'when SaaS', :saas do
context 'when connect-src CSP config exists' do
it { is_expected.to include("connect-src #{connect_src} #{google_analytics_src} #{allowed_src}") }
end
end
end

View File

@ -0,0 +1,34 @@
import { provide } from '~/ci/runner/admin_runners/provide';
import {
onlineContactTimeoutSecs,
staleTimeoutSecs,
runnerInstallHelpPage,
} from 'jest/ci/runner/mock_data';
const mockDataset = {
runnerInstallHelpPage,
onlineContactTimeoutSecs,
staleTimeoutSecs,
};
describe('admin runners provide', () => {
it('returns provide values', () => {
expect(provide(mockDataset)).toMatchObject({
runnerInstallHelpPage,
onlineContactTimeoutSecs,
staleTimeoutSecs,
});
});
it('returns only provide values', () => {
const dataset = {
...mockDataset,
extraEntry: 'ANOTHER_ENTRY',
};
expect(provide(dataset)).not.toMatchObject({
extraEntry: 'ANOTHER_ENTRY',
});
});
});

View File

@ -319,6 +319,7 @@ export const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
export const mockAuthenticationToken = 'MOCK_AUTHENTICATION_TOKEN';
export const newRunnerPath = '/runners/new';
export const runnerInstallHelpPage = 'https://docs.example.com/runner/install/';
export {
allRunnersData,

View File

@ -2,6 +2,7 @@ import { GlEmptyState, GlLoadingIcon, GlTableLite } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
@ -60,10 +61,13 @@ describe('BulkImportsHistoryApp', () => {
let wrapper;
let mock;
const mockRealtimeChangesPath = '/import/realtime_changes.json';
function createComponent({ shallow = true } = {}) {
const mountFn = shallow ? shallowMount : mount;
wrapper = mountFn(BulkImportsHistoryApp);
wrapper = mountFn(BulkImportsHistoryApp, {
provide: { realtimeChangesPath: mockRealtimeChangesPath },
});
}
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
@ -220,4 +224,69 @@ describe('BulkImportsHistoryApp', () => {
expect(JSON.parse(detailsRowContent.text())).toStrictEqual(DUMMY_RESPONSE[1].failures);
});
});
describe('status polling', () => {
describe('when there are no isImporting imports', () => {
it('does not start polling', async () => {
createComponent({ shallow: false });
await waitForPromises();
expect(mock.history.get.map((x) => x.url)).toEqual([API_URL]);
});
});
describe('when there are isImporting imports', () => {
const mockCreatedImport = {
id: 3,
bulk_import_id: 3,
status: 'created',
entity_type: 'group',
source_full_path: 'top-level-group-12',
destination_full_path: 'h5bp/top-level-group-12',
destination_name: 'top-level-group-12',
destination_namespace: 'h5bp',
created_at: '2021-07-08T10:03:44.743Z',
failures: [],
};
const mockImportChanges = [{ id: 3, status_name: 'finished' }];
const pollInterval = 1;
beforeEach(async () => {
const RESPONSE = [mockCreatedImport, ...DUMMY_RESPONSE];
const POLL_HEADERS = { 'poll-interval': pollInterval };
mock.onGet(API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS);
mock.onGet(mockRealtimeChangesPath).replyOnce(HTTP_STATUS_OK, [], POLL_HEADERS);
mock
.onGet(mockRealtimeChangesPath)
.replyOnce(HTTP_STATUS_OK, mockImportChanges, POLL_HEADERS);
createComponent({ shallow: false });
await waitForPromises();
});
it('starts polling for realtime changes', () => {
jest.advanceTimersByTime(pollInterval);
expect(mock.history.get.map((x) => x.url)).toEqual([API_URL, mockRealtimeChangesPath]);
expect(wrapper.findAll('tbody tr').at(0).text()).toContain('Pending');
});
it('stops polling when import is finished', async () => {
jest.advanceTimersByTime(pollInterval);
await waitForPromises();
// Wait an extra interval to make sure we've stopped polling
jest.advanceTimersByTime(pollInterval);
await waitForPromises();
expect(mock.history.get.map((x) => x.url)).toEqual([
API_URL,
mockRealtimeChangesPath,
mockRealtimeChangesPath,
]);
expect(wrapper.findAll('tbody tr').at(0).text()).toContain('Complete');
});
});
});
});

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
RSpec.describe Admin::BroadcastMessagesHelper, feature_category: :onboarding do
include Gitlab::Routing.url_helpers
let_it_be(:user) { create(:user) }
@ -133,6 +133,36 @@ RSpec.describe BroadcastMessagesHelper, feature_category: :onboarding do
end
end
describe '#render_broadcast_message' do
context 'when message is banner' do
let_it_be(:broadcast_message) do
System::BroadcastMessage.new(message: 'Current Message', broadcast_type: :banner)
end.freeze
it 'renders broadcast message' do
expect(helper.render_broadcast_message(broadcast_message)).to eq("<p>Current Message</p>")
end
end
context 'when message is notification' do
let_it_be(:broadcast_message) do
System::BroadcastMessage.new(message: 'Current Message', broadcast_type: :notification)
end.freeze
it 'renders broadcast message' do
expect(helper.render_broadcast_message(broadcast_message)).to eq("<p>Current Message</p>")
end
end
end
describe '#target_access_levels_display' do
let_it_be(:access_levels) { [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER] }.freeze
it 'joins access levels' do
expect(helper.target_access_levels_display(access_levels)).to eq("Reporter, Developer")
end
end
describe '#admin_broadcast_messages_data' do
let(:starts_at) { 1.day.ago }
let(:ends_at) { 1.day.from_now }

View File

@ -83,22 +83,9 @@ RSpec.describe Ci::RunnersHelper, feature_category: :runner_fleet do
end
describe '#admin_runners_data_attributes' do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:instance_runner) { create(:ci_runner, :instance) }
let_it_be(:project_runner) { create(:ci_runner, :project) }
subject { helper.admin_runners_data_attributes }
before do
allow(helper).to receive(:current_user).and_return(admin)
end
it 'returns the data in format' do
expect(helper.admin_runners_data_attributes).to include(
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
registration_token: Gitlab::CurrentSettings.runners_registration_token,
online_contact_timeout_secs: 7200,
stale_timeout_secs: 7889238
)
end
it_behaves_like 'admin_runners_data_attributes contains data'
end
describe '#group_shared_runners_settings_data' do

View File

@ -96,6 +96,26 @@ RSpec.describe Gitlab::MergeRequests::MessageGenerator, feature_category: :code_
end
end
context 'when project has commit template with source project id' do
let(:merge_request) do
double(
:merge_request,
title: 'Fixes',
target_project: project,
source_project: project,
to_reference: '!123',
metrics: nil,
merge_user: nil
)
end
let(message_template_name) { '%{source_project_id}' }
it 'evaluates only necessary variables' do
expect(result_message).to eq project.id.to_s
end
end
context 'when project has commit template with closed issues' do
let(message_template_name) { <<~MSG.rstrip }
Merge branch '%{source_branch}' into '%{target_branch}'

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe API::BroadcastMessages, :aggregate_failures, feature_category: :onboarding do
RSpec.describe API::Admin::BroadcastMessages, :aggregate_failures, feature_category: :onboarding do
let_it_be(:admin) { create(:admin) }
let_it_be(:message) { create(:broadcast_message) }
let_it_be(:path) { '/broadcast_messages' }
@ -17,7 +17,8 @@ RSpec.describe API::BroadcastMessages, :aggregate_failures, feature_category: :o
expect(response).to include_pagination_headers
expect(json_response).to be_kind_of(Array)
expect(json_response.first.keys)
.to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
.to match_array(%w[id message starts_at ends_at color font active target_access_levels target_path
broadcast_type dismissable])
end
end
@ -30,7 +31,8 @@ RSpec.describe API::BroadcastMessages, :aggregate_failures, feature_category: :o
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq message.id
expect(json_response.keys)
.to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
.to match_array(%w[id message starts_at ends_at color font active target_access_levels target_path
broadcast_type dismissable])
end
end
@ -130,6 +132,22 @@ RSpec.describe API::BroadcastMessages, :aggregate_failures, feature_category: :o
expect(response).to have_gitlab_http_status(:created)
expect(json_response['dismissable']).to eq true
end
context 'when create does not persist record' do
let_it_be(:message) { build(:broadcast_message) }.freeze
let_it_be(:stubbed_errors) { ActiveModel::Errors.new(double).tap { |e| e.add(:base, 'error') } }.freeze
before do
allow(System::BroadcastMessage).to receive(:create).and_return(message)
allow(message).to receive(:errors).and_return(stubbed_errors)
end
it 'calls render_validation_error!' do
post api(path, admin, admin_mode: true), params: { message: 'message' }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
end
@ -222,6 +240,23 @@ RSpec.describe API::BroadcastMessages, :aggregate_failures, feature_category: :o
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['dismissable']).to eq true
end
context 'when update fails' do
let_it_be(:message) { build(:broadcast_message) }.freeze
let_it_be(:stubbed_errors) { ActiveModel::Errors.new(double).tap { |e| e.add(:base, 'error') } }.freeze
before do
allow(System::BroadcastMessage).to receive(:find).and_return(message)
allow(message).to receive(:update).and_return(false)
allow(message).to receive(:errors).and_return(stubbed_errors)
end
it 'calls render_validation_error!' do
put api(path, admin, admin_mode: true), params: { message: 'message' }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
end

View File

@ -8150,6 +8150,7 @@
- './spec/requests/admin/version_check_controller_spec.rb'
- './spec/requests/api/access_requests_spec.rb'
- './spec/requests/api/admin/batched_background_migrations_spec.rb'
- './spec/requests/api/admin/broadcast_messages_spec.rb'
- './spec/requests/api/admin/ci/variables_spec.rb'
- './spec/requests/api/admin/instance_clusters_spec.rb'
- './spec/requests/api/admin/plan_limits_spec.rb'
@ -8165,7 +8166,6 @@
- './spec/requests/api/badges_spec.rb'
- './spec/requests/api/boards_spec.rb'
- './spec/requests/api/branches_spec.rb'
- './spec/requests/api/broadcast_messages_spec.rb'
- './spec/requests/api/bulk_imports_spec.rb'
- './spec/requests/api/ci/job_artifacts_spec.rb'
- './spec/requests/api/ci/jobs_spec.rb'

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
RSpec.shared_examples 'admin_runners_data_attributes contains data' do
it 'returns data' do
expect(subject).to include(
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
registration_token: Gitlab::CurrentSettings.runners_registration_token,
online_contact_timeout_secs: 7200,
stale_timeout_secs: 7889238
)
end
end

View File

@ -4,7 +4,14 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport::ReschedulingMethods, feature_category: :importers do
let(:worker) do
Class.new { include(Gitlab::GithubImport::ReschedulingMethods) }.new
Class.new do
def self.name
'MockImportWorker'
end
include ApplicationWorker
include Gitlab::GithubImport::ReschedulingMethods
end.new
end
describe '#perform' do