Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0537e77587
commit
f5eabcfa0e
|
|
@ -26,7 +26,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-show="draftsCount > 0">
|
<div v-show="draftsCount > 0">
|
||||||
<nav class="review-bar-component">
|
<nav class="review-bar-component" data-testid="review_bar_component">
|
||||||
<div
|
<div
|
||||||
class="review-bar-content d-flex gl-justify-content-end"
|
class="review-bar-content d-flex gl-justify-content-end"
|
||||||
data-qa-selector="review_bar_content"
|
data-qa-selector="review_bar_content"
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,13 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
|
||||||
icon: 'approval',
|
icon: 'approval',
|
||||||
tag: '@approved-by',
|
tag: '@approved-by',
|
||||||
},
|
},
|
||||||
|
tokenAlternative: {
|
||||||
|
formattedKey: __('Approved-By'),
|
||||||
|
key: 'approved-by',
|
||||||
|
type: 'string',
|
||||||
|
param: 'usernames',
|
||||||
|
symbol: '@',
|
||||||
|
},
|
||||||
condition: [
|
condition: [
|
||||||
{
|
{
|
||||||
url: 'approved_by_usernames[]=None',
|
url: 'approved_by_usernames[]=None',
|
||||||
|
|
@ -105,7 +112,11 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
|
||||||
|
|
||||||
const tokenPosition = 3;
|
const tokenPosition = 3;
|
||||||
IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvedBy.token]);
|
IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvedBy.token]);
|
||||||
IssuableTokenKeys.tokenKeysWithAlternative.splice(tokenPosition, 0, ...[approvedBy.token]);
|
IssuableTokenKeys.tokenKeysWithAlternative.splice(
|
||||||
|
tokenPosition,
|
||||||
|
0,
|
||||||
|
...[approvedBy.token, approvedBy.tokenAlternative],
|
||||||
|
);
|
||||||
IssuableTokenKeys.conditions.push(...approvedBy.condition);
|
IssuableTokenKeys.conditions.push(...approvedBy.condition);
|
||||||
|
|
||||||
const environmentToken = {
|
const environmentToken = {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
|
||||||
background: $white;
|
|
||||||
z-index: $zindex-dropdown-menu;
|
z-index: $zindex-dropdown-menu;
|
||||||
padding: 7px 0 6px; // to keep aligned with "collapse sidebar" button on the left sidebar
|
display: flex;
|
||||||
border-top: 1px solid $border-color;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: $toggle-sidebar-height;
|
||||||
padding-left: $contextual-sidebar-width;
|
padding-left: $contextual-sidebar-width;
|
||||||
padding-right: $gutter_collapsed_width;
|
padding-right: $gutter_collapsed_width;
|
||||||
|
background: $white;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
transition: padding $sidebar-transition-duration;
|
transition: padding $sidebar-transition-duration;
|
||||||
|
|
||||||
.page-with-icon-sidebar & {
|
.page-with-icon-sidebar & {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class Projects::ForksController < Projects::ApplicationController
|
||||||
feature_category :source_code_management
|
feature_category :source_code_management
|
||||||
|
|
||||||
before_action do
|
before_action do
|
||||||
push_frontend_feature_flag(:fork_project_form)
|
push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class MergeRequestsFinder < IssuableFinder
|
||||||
def filter_negated_items(items)
|
def filter_negated_items(items)
|
||||||
items = super(items)
|
items = super(items)
|
||||||
items = by_negated_reviewer(items)
|
items = by_negated_reviewer(items)
|
||||||
|
items = by_negated_approved_by(items)
|
||||||
by_negated_target_branch(items)
|
by_negated_target_branch(items)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -119,6 +120,12 @@ class MergeRequestsFinder < IssuableFinder
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
def by_negated_approved_by(items)
|
||||||
|
return items unless not_params[:approved_by_usernames]
|
||||||
|
|
||||||
|
items.not_approved_by_users_with_usernames(not_params[:approved_by_usernames])
|
||||||
|
end
|
||||||
|
|
||||||
def source_project_id
|
def source_project_id
|
||||||
@source_project_id ||= params[:source_project_id].presence
|
@source_project_id ||= params[:source_project_id].presence
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ module NavHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def admin_analytics_nav_links
|
def admin_analytics_nav_links
|
||||||
%w(dev_ops_report)
|
%w(dev_ops_report usage_trends)
|
||||||
end
|
end
|
||||||
|
|
||||||
def group_issues_sub_menu_items
|
def group_issues_sub_menu_items
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,19 @@ module ApprovableBase
|
||||||
.group(:id)
|
.group(:id)
|
||||||
.having("COUNT(users.id) = ?", usernames.size)
|
.having("COUNT(users.id) = ?", usernames.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope :not_approved_by_users_with_usernames, -> (usernames) do
|
||||||
|
users = User.where(username: usernames).select(:id)
|
||||||
|
self_table = self.arel_table
|
||||||
|
app_table = Approval.arel_table
|
||||||
|
|
||||||
|
where(
|
||||||
|
Approval.where(approvals: { user_id: users })
|
||||||
|
.where(app_table[:merge_request_id].eq(self_table[:id]))
|
||||||
|
.select('true')
|
||||||
|
.arel.exists.not
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class PlanLimits < ApplicationRecord
|
class PlanLimits < ApplicationRecord
|
||||||
|
include IgnorableColumns
|
||||||
|
|
||||||
|
ignore_column :ci_max_artifact_size_running_container_scanning, remove_with: '14.3', remove_after: '2021-08-22'
|
||||||
|
|
||||||
LimitUndefinedError = Class.new(StandardError)
|
LimitUndefinedError = Class.new(StandardError)
|
||||||
|
|
||||||
belongs_to :plan
|
belongs_to :plan
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ module Projects
|
||||||
# Ensure HEAD points to the default branch in case it is not master
|
# Ensure HEAD points to the default branch in case it is not master
|
||||||
project.change_head(default_branch)
|
project.change_head(default_branch)
|
||||||
|
|
||||||
create_protected_branch if protect_branch?
|
create_protected_branch if protect_branch? && !protected_branch_exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_protected_branch
|
def create_protected_branch
|
||||||
|
|
@ -44,6 +44,10 @@ module Projects
|
||||||
!ProtectedBranch.protected?(project, default_branch)
|
!ProtectedBranch.protected?(project, default_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def protected_branch_exists?
|
||||||
|
project.protected_branches.find_by_name(default_branch).present?
|
||||||
|
end
|
||||||
|
|
||||||
def default_branch
|
def default_branch
|
||||||
project.default_branch
|
project.default_branch
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@
|
||||||
.home-panel-buttons.col-md-12.col-lg-6
|
.home-panel-buttons.col-md-12.col-lg-6
|
||||||
- if current_user
|
- if current_user
|
||||||
.gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
|
.gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
|
||||||
|
- if current_user.admin?
|
||||||
|
= link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon gl-mt-3 gl-mr-2', title: s_('View group in admin area'),
|
||||||
|
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||||
|
= sprite_icon('admin')
|
||||||
- if @notification_setting
|
- if @notification_setting
|
||||||
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top' } }
|
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top' } }
|
||||||
- if can_create_subgroups
|
- if can_create_subgroups
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@
|
||||||
= cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do
|
= cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do
|
||||||
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
|
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
|
||||||
- if current_user
|
- if current_user
|
||||||
|
- if current_user.admin?
|
||||||
|
= link_to [:admin, @project], class: 'btn gl-button btn-icon gl-align-self-start gl-py-2! gl-mr-3', title: s_('View project in admin area'),
|
||||||
|
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||||
|
= sprite_icon('admin')
|
||||||
.gl-display-flex.gl-align-items-start.gl-mr-3
|
.gl-display-flex.gl-align-items-start.gl-mr-3
|
||||||
- if @notification_setting
|
- if @notification_setting
|
||||||
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }
|
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
- page_title s_("ForkProject|Fork project")
|
- page_title s_("ForkProject|Fork project")
|
||||||
|
|
||||||
- if Feature.enabled?(:fork_project_form)
|
- if Feature.enabled?(:fork_project_form, @project, default_enabled: :yaml)
|
||||||
#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
|
#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
|
||||||
endpoint: new_project_fork_path(@project, format: :json),
|
endpoint: new_project_fork_path(@project, format: :json),
|
||||||
new_group_path: new_group_path,
|
new_group_path: new_group_path,
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
:urgency: :low
|
:urgency: :low
|
||||||
:resource_boundary: :unknown
|
:resource_boundary: :unknown
|
||||||
:weight: 1
|
:weight: 1
|
||||||
:idempotent: true
|
:idempotent:
|
||||||
:tags: []
|
:tags: []
|
||||||
- :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range
|
- :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range
|
||||||
:worker_name: AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker
|
:worker_name: AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,55 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module AuthorizedProjectUpdate
|
module AuthorizedProjectUpdate
|
||||||
class UserRefreshFromReplicaWorker < ::AuthorizedProjectsWorker
|
class UserRefreshFromReplicaWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
|
||||||
|
sidekiq_options retry: 3
|
||||||
feature_category :authentication_and_authorization
|
feature_category :authentication_and_authorization
|
||||||
urgency :low
|
urgency :low
|
||||||
queue_namespace :authorized_project_update
|
queue_namespace :authorized_project_update
|
||||||
|
|
||||||
|
# This job will not be deduplicated since it is marked with
|
||||||
|
# `data_consistency :delayed` and not `idempotent!`
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/325291
|
||||||
deduplicate :until_executing, including_scheduled: true
|
deduplicate :until_executing, including_scheduled: true
|
||||||
|
|
||||||
idempotent!
|
data_consistency :delayed
|
||||||
|
|
||||||
# This worker will start reading data from the replica database soon
|
def perform(user_id)
|
||||||
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/333219
|
user = User.find_by_id(user_id)
|
||||||
|
return unless user
|
||||||
|
|
||||||
|
if Feature.enabled?(:user_refresh_from_replica_worker_uses_replica_db)
|
||||||
|
enqueue_project_authorizations_refresh(user) if project_authorizations_needs_refresh?(user)
|
||||||
|
else
|
||||||
|
use_primary_database
|
||||||
|
user.refresh_authorized_projects(source: self.class.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def use_primary_database
|
||||||
|
if ::Gitlab::Database::LoadBalancing.enable?
|
||||||
|
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_authorizations_needs_refresh?(user)
|
||||||
|
AuthorizedProjectUpdate::FindRecordsDueForRefreshService.new(user).needs_refresh?
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_project_authorizations_refresh(user)
|
||||||
|
with_context(user: user, related_class: current_caller_id) do
|
||||||
|
AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker.perform_async(user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We use this so that we can obtain the details of the original caller
|
||||||
|
# in the enqueued `AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker` job.
|
||||||
|
def current_caller_id
|
||||||
|
Gitlab::ApplicationContext.current_context_attribute('meta.caller_id').presence
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321387
|
||||||
milestone: '13.10'
|
milestone: '13.10'
|
||||||
type: development
|
type: development
|
||||||
group: group::source code
|
group: group::source code
|
||||||
default_enabled: false
|
default_enabled: true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: user_refresh_from_replica_worker_uses_replica_db
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64276
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334766
|
||||||
|
milestone: '14.1'
|
||||||
|
type: development
|
||||||
|
group: group::access
|
||||||
|
default_enabled: false
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddPlanLimitsMaxSizeClusterImageScanningColumn < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :plan_limits, :ci_max_artifact_size_cluster_image_scanning, :integer, null: false, default: 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
b37bf7db9c00c8f54c0ccca2d418f1279e12ff7e5b71347966494dc5645eb648
|
||||||
|
|
@ -16352,7 +16352,8 @@ CREATE TABLE plan_limits (
|
||||||
ci_registered_project_runners integer DEFAULT 1000 NOT NULL,
|
ci_registered_project_runners integer DEFAULT 1000 NOT NULL,
|
||||||
web_hook_calls integer DEFAULT 0 NOT NULL,
|
web_hook_calls integer DEFAULT 0 NOT NULL,
|
||||||
ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL,
|
ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL,
|
||||||
ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL
|
ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL,
|
||||||
|
ci_max_artifact_size_cluster_image_scanning integer DEFAULT 0 NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE SEQUENCE plan_limits_id_seq
|
CREATE SEQUENCE plan_limits_id_seq
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ setting is used:
|
||||||
| `ci_max_artifact_size_archive` | 0 |
|
| `ci_max_artifact_size_archive` | 0 |
|
||||||
| `ci_max_artifact_size_browser_performance` | 0 |
|
| `ci_max_artifact_size_browser_performance` | 0 |
|
||||||
| `ci_max_artifact_size_cluster_applications` | 0 |
|
| `ci_max_artifact_size_cluster_applications` | 0 |
|
||||||
|
| `ci_max_artifact_size_cluster_image_scanning` | 0 |
|
||||||
| `ci_max_artifact_size_cobertura` | 0 |
|
| `ci_max_artifact_size_cobertura` | 0 |
|
||||||
| `ci_max_artifact_size_codequality` | 0 |
|
| `ci_max_artifact_size_codequality` | 0 |
|
||||||
| `ci_max_artifact_size_container_scanning` | 0 |
|
| `ci_max_artifact_size_container_scanning` | 0 |
|
||||||
|
|
@ -444,7 +445,6 @@ setting is used:
|
||||||
| `ci_max_artifact_size_network_referee` | 0 |
|
| `ci_max_artifact_size_network_referee` | 0 |
|
||||||
| `ci_max_artifact_size_performance` | 0 |
|
| `ci_max_artifact_size_performance` | 0 |
|
||||||
| `ci_max_artifact_size_requirements` | 0 |
|
| `ci_max_artifact_size_requirements` | 0 |
|
||||||
| `ci_max_artifact_size_running_container_scanning` | 0 |
|
|
||||||
| `ci_max_artifact_size_sast` | 0 |
|
| `ci_max_artifact_size_sast` | 0 |
|
||||||
| `ci_max_artifact_size_secret_detection` | 0 |
|
| `ci_max_artifact_size_secret_detection` | 0 |
|
||||||
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |
|
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |
|
||||||
|
|
|
||||||
|
|
@ -12627,11 +12627,11 @@ Represents summary of a security report.
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="securityreportsummaryapifuzzing"></a>`apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan. |
|
| <a id="securityreportsummaryapifuzzing"></a>`apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan. |
|
||||||
|
| <a id="securityreportsummaryclusterimagescanning"></a>`clusterImageScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `cluster_image_scanning` scan. |
|
||||||
| <a id="securityreportsummarycontainerscanning"></a>`containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan. |
|
| <a id="securityreportsummarycontainerscanning"></a>`containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan. |
|
||||||
| <a id="securityreportsummarycoveragefuzzing"></a>`coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan. |
|
| <a id="securityreportsummarycoveragefuzzing"></a>`coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan. |
|
||||||
| <a id="securityreportsummarydast"></a>`dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan. |
|
| <a id="securityreportsummarydast"></a>`dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan. |
|
||||||
| <a id="securityreportsummarydependencyscanning"></a>`dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan. |
|
| <a id="securityreportsummarydependencyscanning"></a>`dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan. |
|
||||||
| <a id="securityreportsummaryrunningcontainerscanning"></a>`runningContainerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `running_container_scanning` scan. |
|
|
||||||
| <a id="securityreportsummarysast"></a>`sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan. |
|
| <a id="securityreportsummarysast"></a>`sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan. |
|
||||||
| <a id="securityreportsummarysecretdetection"></a>`secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan. |
|
| <a id="securityreportsummarysecretdetection"></a>`secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan. |
|
||||||
|
|
||||||
|
|
@ -13486,7 +13486,7 @@ Represents a vulnerability.
|
||||||
| <a id="vulnerabilitynotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
|
| <a id="vulnerabilitynotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
|
||||||
| <a id="vulnerabilityprimaryidentifier"></a>`primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. |
|
| <a id="vulnerabilityprimaryidentifier"></a>`primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. |
|
||||||
| <a id="vulnerabilityproject"></a>`project` | [`Project`](#project) | The project on which the vulnerability was found. |
|
| <a id="vulnerabilityproject"></a>`project` | [`Project`](#project) | The project on which the vulnerability was found. |
|
||||||
| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, RUNNING_CONTAINER_SCANNING). `Scan Type` in the UI. |
|
| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, CLUSTER_IMAGE_SCANNING). `Scan Type` in the UI. |
|
||||||
| <a id="vulnerabilityresolvedat"></a>`resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state was changed to resolved. |
|
| <a id="vulnerabilityresolvedat"></a>`resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state was changed to resolved. |
|
||||||
| <a id="vulnerabilityresolvedby"></a>`resolvedBy` | [`UserCore`](#usercore) | The user that resolved the vulnerability. |
|
| <a id="vulnerabilityresolvedby"></a>`resolvedBy` | [`UserCore`](#usercore) | The user that resolved the vulnerability. |
|
||||||
| <a id="vulnerabilityresolvedondefaultbranch"></a>`resolvedOnDefaultBranch` | [`Boolean!`](#boolean) | Indicates whether the vulnerability is fixed on the default branch or not. |
|
| <a id="vulnerabilityresolvedondefaultbranch"></a>`resolvedOnDefaultBranch` | [`Boolean!`](#boolean) | Indicates whether the vulnerability is fixed on the default branch or not. |
|
||||||
|
|
@ -15162,11 +15162,11 @@ The type of the security scan that found the vulnerability.
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ----- | ----------- |
|
| ----- | ----------- |
|
||||||
| <a id="vulnerabilityreporttypeapi_fuzzing"></a>`API_FUZZING` | |
|
| <a id="vulnerabilityreporttypeapi_fuzzing"></a>`API_FUZZING` | |
|
||||||
|
| <a id="vulnerabilityreporttypecluster_image_scanning"></a>`CLUSTER_IMAGE_SCANNING` | |
|
||||||
| <a id="vulnerabilityreporttypecontainer_scanning"></a>`CONTAINER_SCANNING` | |
|
| <a id="vulnerabilityreporttypecontainer_scanning"></a>`CONTAINER_SCANNING` | |
|
||||||
| <a id="vulnerabilityreporttypecoverage_fuzzing"></a>`COVERAGE_FUZZING` | |
|
| <a id="vulnerabilityreporttypecoverage_fuzzing"></a>`COVERAGE_FUZZING` | |
|
||||||
| <a id="vulnerabilityreporttypedast"></a>`DAST` | |
|
| <a id="vulnerabilityreporttypedast"></a>`DAST` | |
|
||||||
| <a id="vulnerabilityreporttypedependency_scanning"></a>`DEPENDENCY_SCANNING` | |
|
| <a id="vulnerabilityreporttypedependency_scanning"></a>`DEPENDENCY_SCANNING` | |
|
||||||
| <a id="vulnerabilityreporttyperunning_container_scanning"></a>`RUNNING_CONTAINER_SCANNING` | |
|
|
||||||
| <a id="vulnerabilityreporttypesast"></a>`SAST` | |
|
| <a id="vulnerabilityreporttypesast"></a>`SAST` | |
|
||||||
| <a id="vulnerabilityreporttypesecret_detection"></a>`SECRET_DETECTION` | |
|
| <a id="vulnerabilityreporttypesecret_detection"></a>`SECRET_DETECTION` | |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,30 @@ def down
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Multiple changes on the same table:**
|
||||||
|
|
||||||
|
The helper `with_lock_retries` wraps all operations into a single transaction. When you have the lock,
|
||||||
|
you should do as much as possible inside the transaction rather than trying to get another lock later.
|
||||||
|
Be careful about running long database statements within the block. The acquired locks are kept until the transaction (block) finishes and depending on the lock type, it might block other database operations.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
def up
|
||||||
|
with_lock_retries do
|
||||||
|
add_column :users, :full_name, :string
|
||||||
|
add_column :users, :bio, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
with_lock_retries do
|
||||||
|
remove_column :users, :full_name
|
||||||
|
remove_column :users, :bio
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
**Removing a foreign key:**
|
**Removing a foreign key:**
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
|
|
||||||
|
|
@ -17318,6 +17318,18 @@ Status: `data_available`
|
||||||
|
|
||||||
Tiers:
|
Tiers:
|
||||||
|
|
||||||
|
### `usage_activity_by_stage.secure.cluster_image_scanning_scans`
|
||||||
|
|
||||||
|
Counts cluster image scanning jobs
|
||||||
|
|
||||||
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_cluster_image_scanning_scans.yml)
|
||||||
|
|
||||||
|
Group: `group::container security`
|
||||||
|
|
||||||
|
Status: `implemented`
|
||||||
|
|
||||||
|
Tiers: `ultimate`
|
||||||
|
|
||||||
### `usage_activity_by_stage.secure.container_scanning_scans`
|
### `usage_activity_by_stage.secure.container_scanning_scans`
|
||||||
|
|
||||||
Counts container scanning jobs
|
Counts container scanning jobs
|
||||||
|
|
@ -17366,18 +17378,6 @@ Status: `data_available`
|
||||||
|
|
||||||
Tiers: `ultimate`
|
Tiers: `ultimate`
|
||||||
|
|
||||||
### `usage_activity_by_stage.secure.running_container_scanning_scans`
|
|
||||||
|
|
||||||
Counts running container scanning jobs
|
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_running_container_scanning_scans.yml)
|
|
||||||
|
|
||||||
Group: `group::container security`
|
|
||||||
|
|
||||||
Status: `data_available`
|
|
||||||
|
|
||||||
Tiers: `ultimate`
|
|
||||||
|
|
||||||
### `usage_activity_by_stage.secure.sast_scans`
|
### `usage_activity_by_stage.secure.sast_scans`
|
||||||
|
|
||||||
Counts sast jobs
|
Counts sast jobs
|
||||||
|
|
@ -19394,6 +19394,30 @@ Status: `data_available`
|
||||||
|
|
||||||
Tiers: `free`
|
Tiers: `free`
|
||||||
|
|
||||||
|
### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_pipeline`
|
||||||
|
|
||||||
|
Pipelines containing a Cluster Image Scanning job
|
||||||
|
|
||||||
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_cluster_image_scanning_pipeline.yml)
|
||||||
|
|
||||||
|
Group: `group::container security`
|
||||||
|
|
||||||
|
Status: `implemented`
|
||||||
|
|
||||||
|
Tiers: `ultimate`
|
||||||
|
|
||||||
|
### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_scans`
|
||||||
|
|
||||||
|
Counts cluster image scanning jobs
|
||||||
|
|
||||||
|
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_cluster_image_scanning_scans.yml)
|
||||||
|
|
||||||
|
Group: `group::container security`
|
||||||
|
|
||||||
|
Status: `implemented`
|
||||||
|
|
||||||
|
Tiers: `ultimate`
|
||||||
|
|
||||||
### `usage_activity_by_stage_monthly.secure.container_scanning_pipeline`
|
### `usage_activity_by_stage_monthly.secure.container_scanning_pipeline`
|
||||||
|
|
||||||
Pipelines containing a Container Scanning job
|
Pipelines containing a Container Scanning job
|
||||||
|
|
@ -19490,30 +19514,6 @@ Status: `data_available`
|
||||||
|
|
||||||
Tiers: `ultimate`
|
Tiers: `ultimate`
|
||||||
|
|
||||||
### `usage_activity_by_stage_monthly.secure.running_container_scanning_pipeline`
|
|
||||||
|
|
||||||
Pipelines containing a Running Container Scanning job
|
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_running_container_scanning_pipeline.yml)
|
|
||||||
|
|
||||||
Group: `group::container security`
|
|
||||||
|
|
||||||
Status: `data_available`
|
|
||||||
|
|
||||||
Tiers: `ultimate`
|
|
||||||
|
|
||||||
### `usage_activity_by_stage_monthly.secure.running_container_scanning_scans`
|
|
||||||
|
|
||||||
Counts running container scanning jobs
|
|
||||||
|
|
||||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_running_container_scanning_scans.yml)
|
|
||||||
|
|
||||||
Group: `group::container security`
|
|
||||||
|
|
||||||
Status: `data_available`
|
|
||||||
|
|
||||||
Tiers: `ultimate`
|
|
||||||
|
|
||||||
### `usage_activity_by_stage_monthly.secure.sast_pipeline`
|
### `usage_activity_by_stage_monthly.secure.sast_pipeline`
|
||||||
|
|
||||||
Counts of Pipelines that have at least 1 SAST job
|
Counts of Pipelines that have at least 1 SAST job
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ module API
|
||||||
|
|
||||||
before { authenticated_as_admin! }
|
before { authenticated_as_admin! }
|
||||||
|
|
||||||
feature_category :continuous_integration
|
feature_category :pipeline_authoring
|
||||||
|
|
||||||
namespace 'admin' do
|
namespace 'admin' do
|
||||||
namespace 'ci' do
|
namespace 'ci' do
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ module API
|
||||||
|
|
||||||
content_type :txt, 'text/plain'
|
content_type :txt, 'text/plain'
|
||||||
|
|
||||||
feature_category :continuous_integration
|
feature_category :runner
|
||||||
|
|
||||||
resource :runners do
|
resource :runners do
|
||||||
desc 'Registers a new Runner' do
|
desc 'Registers a new Runner' do
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ module API
|
||||||
before { authenticate! }
|
before { authenticate! }
|
||||||
before { authorize! :admin_build, user_project }
|
before { authorize! :admin_build, user_project }
|
||||||
|
|
||||||
feature_category :continuous_integration
|
feature_category :pipeline_authoring
|
||||||
|
|
||||||
helpers Helpers::VariablesHelpers
|
helpers Helpers::VariablesHelpers
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35860,6 +35860,9 @@ msgstr ""
|
||||||
msgid "View full log"
|
msgid "View full log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "View group in admin area"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "View group labels"
|
msgid "View group labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -35911,6 +35914,9 @@ msgstr ""
|
||||||
msgid "View project"
|
msgid "View project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "View project in admin area"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "View project labels"
|
msgid "View project labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has review bar' do
|
it 'has review bar' do
|
||||||
expect(page).to have_css('.review-bar-component', visible: false)
|
expect(page).to have_selector('[data-testid="review_bar_component"]', visible: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds draft note' do
|
it 'adds draft note' do
|
||||||
|
|
@ -32,7 +32,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
|
||||||
|
|
||||||
expect(find('.draft-note-component')).to have_content('Line is wrong')
|
expect(find('.draft-note-component')).to have_content('Line is wrong')
|
||||||
|
|
||||||
expect(page).to have_css('.review-bar-component')
|
expect(page).to have_selector('[data-testid="review_bar_component"]')
|
||||||
|
|
||||||
expect(find('.review-bar-content .btn-confirm')).to have_content('1')
|
expect(find('.review-bar-content .btn-confirm')).to have_content('1')
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -520,6 +520,44 @@ RSpec.describe MergeRequestsFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'filtering by approved by' do
|
||||||
|
let(:params) { { approved_by_usernames: user2.username } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:approval, merge_request: merge_request3, user: user2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns merge requests approved by that user' do
|
||||||
|
merge_requests = described_class.new(user, params).execute
|
||||||
|
|
||||||
|
expect(merge_requests).to contain_exactly(merge_request3)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'not filter' do
|
||||||
|
let(:params) { { not: { approved_by_usernames: user2.username } } }
|
||||||
|
|
||||||
|
it 'returns merge requests not approved by that user' do
|
||||||
|
merge_requests = described_class.new(user, params).execute
|
||||||
|
|
||||||
|
expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request4, merge_request5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when filtering by author and not approved by' do
|
||||||
|
let(:params) { { not: { approved_by_usernames: user2.username }, author_username: user.username } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
merge_request4.update!(author: user2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns merge requests authored by user and not approved by user2' do
|
||||||
|
merge_requests = described_class.new(user, params).execute
|
||||||
|
|
||||||
|
expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'filtering by created_at/updated_at' do
|
context 'filtering by created_at/updated_at' do
|
||||||
let(:new_project) { create(:project, forked_from_project: project1) }
|
let(:new_project) { create(:project, forked_from_project: project1) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,4 +59,25 @@ RSpec.describe ApprovableBase do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.not_approved_by_users_with_usernames' do
|
||||||
|
subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
|
||||||
|
|
||||||
|
let!(:merge_request2) { create(:merge_request) }
|
||||||
|
let!(:merge_request3) { create(:merge_request) }
|
||||||
|
let!(:merge_request4) { create(:merge_request) }
|
||||||
|
let(:user2) { create(:user) }
|
||||||
|
let(:user3) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:approval, merge_request: merge_request, user: user)
|
||||||
|
create(:approval, merge_request: merge_request2, user: user2)
|
||||||
|
create(:approval, merge_request: merge_request2, user: user3)
|
||||||
|
create(:approval, merge_request: merge_request4, user: user3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the merge request that is not approved at all and not approved by either user' do
|
||||||
|
expect(subject).to contain_exactly(merge_request3, merge_request4)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ RSpec.describe PlanLimits do
|
||||||
ci_max_artifact_size_junit
|
ci_max_artifact_size_junit
|
||||||
ci_max_artifact_size_sast
|
ci_max_artifact_size_sast
|
||||||
ci_max_artifact_size_dast
|
ci_max_artifact_size_dast
|
||||||
ci_max_artifact_size_running_container_scanning
|
ci_max_artifact_size_cluster_image_scanning
|
||||||
ci_max_artifact_size_codequality
|
ci_max_artifact_size_codequality
|
||||||
ci_max_artifact_size_license_management
|
ci_max_artifact_size_license_management
|
||||||
ci_max_artifact_size_performance
|
ci_max_artifact_size_performance
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,53 @@ RSpec.describe Projects::ProtectDefaultBranchService do
|
||||||
.not_to have_received(:create_protected_branch)
|
.not_to have_received(:create_protected_branch)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when protected branch does not exist' do
|
||||||
|
before do
|
||||||
|
allow(service)
|
||||||
|
.to receive(:protected_branch_exists?)
|
||||||
|
.and_return(false)
|
||||||
|
allow(service)
|
||||||
|
.to receive(:protect_branch?)
|
||||||
|
.and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'changes the HEAD of the project' do
|
||||||
|
service.protect_default_branch
|
||||||
|
|
||||||
|
expect(project)
|
||||||
|
.to have_received(:change_head)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'protects the default branch' do
|
||||||
|
service.protect_default_branch
|
||||||
|
|
||||||
|
expect(service)
|
||||||
|
.to have_received(:create_protected_branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when protected branch already exists' do
|
||||||
|
before do
|
||||||
|
allow(service)
|
||||||
|
.to receive(:protected_branch_exists?)
|
||||||
|
.and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'changes the HEAD of the project' do
|
||||||
|
service.protect_default_branch
|
||||||
|
|
||||||
|
expect(project)
|
||||||
|
.to have_received(:change_head)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not protect the default branch' do
|
||||||
|
service.protect_default_branch
|
||||||
|
|
||||||
|
expect(service)
|
||||||
|
.not_to have_received(:create_protected_branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#create_protected_branch' do
|
describe '#create_protected_branch' do
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,30 @@ RSpec.describe 'groups/_home_panel' do
|
||||||
|
|
||||||
expect(rendered).to have_content("Group ID: #{group.id}")
|
expect(rendered).to have_content("Group ID: #{group.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'admin area link' do
|
||||||
|
it 'renders admin area link for admin' do
|
||||||
|
allow(view).to receive(:current_user).and_return(create(:admin))
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_link(href: admin_group_path(group))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not render admin area link for non-admin' do
|
||||||
|
allow(view).to receive(:current_user).and_return(create(:user))
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).not_to have_link(href: admin_group_path(group))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not render admin area link for anonymous' do
|
||||||
|
allow(view).to receive(:current_user).and_return(nil)
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).not_to have_link(href: admin_group_path(group))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,38 @@ require 'spec_helper'
|
||||||
RSpec.describe 'projects/_home_panel' do
|
RSpec.describe 'projects/_home_panel' do
|
||||||
include ProjectForksHelper
|
include ProjectForksHelper
|
||||||
|
|
||||||
|
context 'admin area link' do
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
assign(:project, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders admin area link for admin' do
|
||||||
|
allow(view).to receive(:current_user).and_return(create(:admin))
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_link(href: admin_project_path(project))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not render admin area link for non-admin' do
|
||||||
|
allow(view).to receive(:current_user).and_return(create(:user))
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).not_to have_link(href: admin_project_path(project))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not render admin area link for anonymous' do
|
||||||
|
allow(view).to receive(:current_user).and_return(nil)
|
||||||
|
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).not_to have_link(href: admin_project_path(project))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'notifications' do
|
context 'notifications' do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,97 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe AuthorizedProjectUpdate::UserRefreshFromReplicaWorker do
|
RSpec.describe AuthorizedProjectUpdate::UserRefreshFromReplicaWorker do
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
let_it_be(:user) { project.namespace.owner }
|
||||||
|
|
||||||
|
let(:execute_worker) { subject.perform(user.id) }
|
||||||
|
|
||||||
it 'is labeled as low urgency' do
|
it 'is labeled as low urgency' do
|
||||||
expect(described_class.get_urgency).to eq(:low)
|
expect(described_class.get_urgency).to eq(:low)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like "refreshes user's project authorizations"
|
it_behaves_like 'worker with data consistency',
|
||||||
|
described_class,
|
||||||
|
data_consistency: :delayed
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
it 'checks if a project_authorization refresh is needed for the user' do
|
||||||
|
expect(AuthorizedProjectUpdate::FindRecordsDueForRefreshService).to(
|
||||||
|
receive(:new).with(user).and_call_original)
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are project authorization records due for either removal or addition for a specific user' do
|
||||||
|
before do
|
||||||
|
user.project_authorizations.delete_all
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'enqueues a new project authorization update job for the user' do
|
||||||
|
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id)
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'setting `meta.caller_id` as `meta.related_class` in the context of the newly enqueued `UserRefreshWithLowUrgencyWorker` job' do
|
||||||
|
context 'when the `UserRefreshFromReplicaWorker` job has a `caller_id` set' do
|
||||||
|
it 'sets the same `caller_id` as `related_class`' do
|
||||||
|
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do
|
||||||
|
expect(Gitlab::ApplicationContext.current).to include('meta.related_class' => 'Foo')
|
||||||
|
end
|
||||||
|
|
||||||
|
Gitlab::ApplicationContext.with_context(caller_id: 'Foo') do
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the `UserRefreshFromReplicaWorker` job does not have a `caller_id` set' do
|
||||||
|
it 'does not set the value of `related_class`' do
|
||||||
|
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do
|
||||||
|
expect(Gitlab::ApplicationContext.current).not_to include('meta.related_class')
|
||||||
|
end
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no additions or removals to be made to project authorizations for a specific user' do
|
||||||
|
it 'does not enqueue a new project authorization update job for the user' do
|
||||||
|
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).not_to receive(:perform_async)
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the feature flag `user_refresh_from_replica_worker_uses_replica_db` is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(user_refresh_from_replica_worker_uses_replica_db: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when load balancing is enabled' do
|
||||||
|
before do
|
||||||
|
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reads from the primary database' do
|
||||||
|
expect(Gitlab::Database::LoadBalancing::Session.current)
|
||||||
|
.to receive(:use_primary!)
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls Users::RefreshAuthorizedProjectsService' do
|
||||||
|
source = 'AuthorizedProjectUpdate::UserRefreshFromReplicaWorker'
|
||||||
|
expect_next_instance_of(Users::RefreshAuthorizedProjectsService, user, { source: source }) do |service|
|
||||||
|
expect(service).to receive(:execute)
|
||||||
|
end
|
||||||
|
|
||||||
|
execute_worker
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue