Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
76c4dd062c
commit
f605b80ff7
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
|
||||
.md-preview-holder {
|
||||
min-height: 173px;
|
||||
min-height: 177px;
|
||||
padding: 10px 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module Registrations
|
|||
class WelcomeController < ApplicationController
|
||||
include OneTrustCSP
|
||||
include GoogleAnalyticsCSP
|
||||
include GoogleSyndicationCSP
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
layout 'minimal'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class SessionsController < Devise::SessionsController
|
|||
include BizibleCSP
|
||||
include VerifiesWithEmail
|
||||
include GoogleAnalyticsCSP
|
||||
include GoogleSyndicationCSP
|
||||
include PreferredLanguageSwitcher
|
||||
include SkipsAlreadySignedInMessage
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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) } }
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
e171f06f9b1a0824cd11216255b1f99976bdd97069a35a8514934d33e5b3e634
|
||||
|
|
@ -0,0 +1 @@
|
|||
f71b5244de5b3ed97e38414b304df80fd94ce0855a624efd178ffedfa36dfd31
|
||||
|
|
@ -0,0 +1 @@
|
|||
09f9ee7ddf7153dbdb151f15a9de245d722a908b67b799da8a80f1bd9cb93c31
|
||||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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` |
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue